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值,那么插值的项数就不够了,导致了循环卷积。

猜你喜欢

转载自www.cnblogs.com/autoint/p/12013923.html