CF300D Painting Square

Painting Square

https://codeforces.com/problemset/problem/300/D

给了一个理解起来较复杂但是本质上很简单的分形。

题解

很显然,只有边长为大于等于3且为奇数的正方形能被操作,并且每一次的操作方式是唯一的:沿中心将正方形分成四个等大的小正方形。

我们令操作次数为M,表示大小为N的正方形可以被分割缩小的次数(即每次分割一个存在的最小的正方形),\(dp_{M,K}\)表示一个操作次数为M的正方形中,操作K次的方案数。

则有:

\[dp_{M,K}=\sum_{i+j+k+l=K−1}dp_{M−1,i}∗dp_{M−1,j}∗dp_{M−1,k}∗dp_{M−1,l} \]

7340033是NTT模数,原根为3,NTT卷积转移即可。

时间复杂度O(Q log N+K log N log K)。

CO int N=4096;
int dp[31][N],tmp[N];
int rev[N],omg[N];

void NTT(int a[],int lim){
	for(int i=0;i<lim;++i)
		if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<lim;i<<=1)
		for(int j=0;j<lim;j+=i<<1)
			for(int k=0;k<i;++k){
				int t=mul(omg[lim/(i<<1)*k],a[j+i+k]);
				a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
			}
}
int main(){
	int len=log2(N),lim=N;
	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
	omg[0]=1,omg[1]=fpow(3,(mod-1)/lim);
	for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
	dp[0][0]=1;
	for(int i=1;i<31;++i){
		copy(dp[i-1],dp[i-1]+lim,tmp);
		NTT(tmp,lim);
		for(int j=0;j<lim;++j) tmp[j]=fpow(tmp[j],4);
		omg[0]=1,omg[1]=fpow(omg[1],mod-2);
		for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
		NTT(tmp,lim);
		dp[i][0]=1;
		int ilim=fpow(lim,mod-2);
		for(int j=1;j<1005;++j) dp[i][j]=mul(tmp[j-1],ilim); // edit 1
		omg[0]=1,omg[1]=fpow(omg[1],mod-2);
		for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
	}
	for(int q=read<int>();q--;){
		int n=read<int>(),k=read<int>();
		int m=0;
		for(;n>1 and n&1;n>>=1) ++m;
		printf("%d\n",dp[m][k]);
	}
	return 0;
}

尽管暴力卷积的时候第二维大于1000的DP值无关紧要,但是DP记录的时候只能记录前1000个,记录后面的就会错。这大概是因为如果记录了后面的DP值,那么插值的项数就不够了,导致了循环卷积。

posted on 2019-12-09 22:39  autoint  阅读(179)  评论(0编辑  收藏  举报

导航