E:Stop. Otherwise...
题意:
有(n)个取值为([1,k])的骰子,对于每一个(i(iin[2,2k])) ,输出满足"任意两个骰子的值的和不为 i"的情况总数,其中不同顺序算相同方案
(1 le n,k le 2000)
题解:
其实还是很容易想到的
考虑对于一个i的答案的计数
将其分为小于(i)的数,大于等于(i)的数
对于大于等于(i)的数
显然怎么选都可以,但要注意顺序,随便想一想就知道答案是(inom{n+m-1}{m-1}),n是选的数的数量,m是有多少种种类的数
对于小于(i)的数,
对于一个(i),如果选了(j),就不能选(i-j),那么我们直接把贡献转到([1,lfloor frac{i}{2}
floor])
考虑(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
",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
",ans[i]);
for(int i=K;i>=2;i--)
printf("%lld
",ans[i]);
return 0;
}
用时:40min