CF1628D2 Game on Sum

发布时间 2023-10-15 16:56:30作者: Trmp

题目链接(Easy)

题目链接(Hard)

Part1

神奇的博弈类型 \(Dp\)

我们发现与当前状态有关的量,有且只有 现在是第几轮,还有 Bob 用了几次加的操作 ,这都会影响之后的决策,而和之前的决策无关,换句话说,当前决策有后效性,没有前效性。那我们考虑倒着 \(Dp\).

Part2

\(f_{i,j}\) 表示还有 \(i\) 轮,且 \(Bob\) 还要用 \(j\) 次加操作,考虑当前选择加操作还是减操作, 如果是加操作,有 \(f_{i,j}=f_{i-1,j-1}+x\), 如果是减操作,有 \(f_{i,j}=f_{i-1,j}-x\), 对于 \(Bob\) 而言,两者取 \(\min\) ,而 \(Alice\) 为了使结果最大,她会改变 \(x\) ,让 \(f_{i-1,j-1}+x=f_{i-1,j}-x\),解得 $x=\frac{f_{i-1,j}-f_{i-1,j-1}}{2} $ ,那么有转移:

\[f_{i,j}=\frac{f_{i-1,j}+f_{i-1,j-1}}{2} \]

初始状态 \(f_{0,0}=0\), \(f_{i,i}=i*k\)

直接暴力转移,复杂度 \(O(nm)\) ,可以通过 \(Easy\).

Part3

考虑优化,发现对答案相当于每个 \(f_{i,i}\) 的累加,考虑计数。

想象一个平面直角坐标系的第一象限,\(f_{i,i}\) 会对他的右边和右上方产生贡献,那么每个 \(f_{i,i}\) 对答案产生的贡献相当于从 \((i+1,i)\) ,每次走右边或者右上,走到 \((n,m)\) 的方案数,不是 \((i,i)\) 的原因是 \(f_{i,i}\) 不可以对 \(f_{i+1,i+1}\) 产生贡献,相当于第一步只能走右边,而不能走右上。

每次走必定向右走一格,所以一共可以走 \((n-(i+1))\) 步,其中任意选 \((m-i)\) 步改为 右上,所以方案数为 \(\dbinom{n-i-1}{m-i}\),再除以 \(2^{n-i}\)

答案即为:

\[k\sum^{m}_{i=1} \frac{i·\dbinom{n-i-1}{m-i} }{2^{n-i}} \]

复杂度 \(O(m)\)

Code
#include <iostream>
#include <cstdio>

#define int long long

const int N=1e6+10;
const int mod=1e9+7;

using namespace std;

inline int read() {
    int f=1, x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f*x;
}

inline void write(int x) {
    cout<<x; putchar('\n');
}

int T, n, m, k;
int fac[N], inv[N], invj[N], power[N];

void prework() {
    fac[0]=fac[1]=inv[0]=inv[1]=invj[0]=invj[1]=1;
    for(int i=2;i<=N-5;i++) {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        invj[i]=invj[i-1]*inv[i]%mod;
    }
    power[0]=1;
    for(int i=1;i<=N-5;i++) power[i]=power[i-1]*inv[2]%mod;
}

inline int C(int n,int m) {
    if(m>n) return 0;
    return fac[n]*invj[m]%mod*invj[n-m]%mod;
}

signed main() {

    prework();

    T=read();
    while(T--) {
        n=read(), m=read() ,k=read();

        if(n==m) {
            write(m*k%mod);
            continue;
        }

        int ans=0;
        for(int i=1;i<=m;i++) {
            int sum=i*C(n-i-1,m-i)%mod*power[n-i]%mod;
            ans=(ans+sum)%mod;
        }
        write(ans*k%mod);

    }

    return 0;
}