[ARC 102E]Stop. Otherwise...

Atcoder ARC 102E

E:Stop. Otherwise...

题意:

\(n\)个取值为\([1,k]\)的骰子,对于每一个\(i(i\in[2,2k])\) ,输出满足"任意两个骰子的值的和不为 i"的情况总数,其中不同顺序算相同方案
\(1 \le n,k \le 2000\)

题解:

其实还是很容易想到的
考虑对于一个i的答案的计数
将其分为小于\(i\)的数,大于等于\(i\)的数

对于大于等于\(i\)的数

显然怎么选都可以,但要注意顺序,随便想一想就知道答案是\(\binom{n+m-1}{m-1}\),n是选的数的数量,m是有多少种种类的数

对于小于\(i\)的数,

对于一个\(i\),如果选了\(j\),就不能选\(i-j\),那么我们直接把贡献转到\([1,\lfloor \frac{i}{2} \rfloor]\)
考虑\(f_{i,j}\)表示用了\(i\)个数,最多能选到数字\(j\)的方案数(不一定\([1,j]\)中每个数字都要选)
那么\(f_{i,j}=2 \sum_{k=0}^{i-1}f_{k,j-1}+f_{i,j-1}\) 意义就是枚举第j个数选了多少个,如果选了超过0个那就可以放到j和j对应的那个位置,所以乘2,否则就不放,不能乘2.
还有一种特殊情况:当i是偶数的时候\(\frac{i}{2}\)最多只能选1个,直接枚举这个就行了

过程:

各种智障错误,不会写代码了

代码:

const int N=4010;
const ll P=998244353;
int K,n;
ll f[N][N],g[N];
ll C[N][N];
ll ans[N];
signed main() {
    read(K); read(n);
    for(int j=0;j<=K;j++) f[0][j]=1;
    for(int i=0;i<=n;i++) g[i]=1;
    for(int j=1;j<=K;j++) {
        for(int i=1;i<=n;i++) {
            f[i][j]=(g[i-1]*2%P+f[i][j-1])%P;
            if(i!=1) g[i-1]=(g[i-2]+f[i-1][j])%P;
        }
    }
    for(int i=0;i<=n+K;i++) C[i][0]=1;
    for(int i=1;i<=n+K;i++)
        for(int j=0;j<=K;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
    for(int k=2;k<=K;k++) {
        if(k&1) {
            for(int i=0;i<=n;i++) {
                (ans[k]+=f[i][k/2]*C[n-i + K-k][K-k]%P)%=P;
            }
        } else {
            for(int i=0;i<=n;i++) {
                (ans[k]+=f[i][k/2-1]*((C[n-i + K-k][K-k]+(i==n ? 0 : C[n-i-1 + K-k][K-k]))%P)%P)%=P;
                // if(k==4) printf("%lld %lld\n",f[i][k/2-1],(C[n-i + K-k][K-k]+(i==n ? 0 : C[n-i-1 + K-k][K-k])));
            }
        }
        assert(ans[k]>=0);
    }
    if((K+1)&1) {
        ans[K+1]=f[n][(K+1)/2];
    } else {
        ans[K+1]=(f[n][(K+1)/2-1]+f[n-1][(K+1)/2-1])%P;
    }
    for(int i=2;i<=K+1;i++)
        printf("%lld\n",ans[i]);
    for(int i=K;i>=2;i--)
        printf("%lld\n",ans[i]);
    return 0;
}

用时:40min

猜你喜欢

转载自www.cnblogs.com/functionendless/p/9574862.html
arc
今日推荐