[ARC 102E]Stop. Otherwise... E:Stop. Otherwise...

Atcoder ARC 102E

题意:

(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