[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

posted @ 2018-09-02 19:17  functionendless  阅读(174)  评论(0编辑  收藏  举报