zoukankan      html  css  js  c++  java
  • Codechef Devu and Locks

    Devu and Locks

    求有多少(n)位十进制数(可以有前导(0)),模(p = 0),各个数位之和不超过(m)

    (998244353)

    (n ≤ 10^9, p ≤ 16, m ≤ 15000)

    题解

    任轩笛《杂题选讲》。

    就是个倍增二维FFT。

    (f_{i,j,k})表示(2^i)位数,(mod p=j),数位和是(k)的方案数。

    [f_{i,j_1,k_1} imes f_{i,j_2,k_2} ightarrow f_{i+1,(j_1 imes 10^{2^i}+j_2)mod p,k_1+k_2} ]

    时间复杂度(O(pmlog(pm)log n))

    typedef vector<poly> poly2;
    
    CO int N=1<<15;
    int omg[2][N],rev[N];
    
    void NTT(poly&F,int dir){
    	int lim=F.size(),len=log2(lim);
    	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    	for(int i=0;i<lim;++i)if(i<rev[i]) swap(F[i],F[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[dir][N/(i<<1)*k],F[j+i+k]);
    			F[j+i+k]=add(F[j+k],mod-t),F[j+k]=add(F[j+k],t);
    		}
    	if(dir){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) F[i]=mul(F[i],ilim);
    	}
    }
    poly operator+(CO poly&F,CO poly&G){
    	int n=F.size()-1,m=G.size()-1;
    	poly H(max(n,m)+1);
    	for(int i=0;i<=max(n,m);++i)
    		H[i]=add(i<=n?F[i]:0,i<=m?G[i]:0);
    	return H;
    }
    poly2 reverse(CO poly2&F){
    	int n1=F.size()-1,n2=F.front().size()-1;
    	poly2 G(n2+1,poly(n1+1));
    	for(int i=0;i<=n1;++i)for(int j=0;j<=n2;++j)
    		G[j][i]=F[i][j];
    	return G;
    }
    poly2 operator*(poly2 F,poly2 G){
    	int n1=F.size()-1,n2=F.front().size()-1;
    	int m1=G.size()-1,m2=G.front().size()-1;
    	int lim1=1<<(int)ceil(log2(n1+m1+1)),lim2=1<<(int)ceil(log2(n2+m2+1));
    	F.resize(lim1),G.resize(lim1);
    	for(int i=0;i<lim1;++i) F[i].resize(lim2),G[i].resize(lim2);
    	for(int i=0;i<lim1;++i) NTT(F[i],0),NTT(G[i],0);
    	F=reverse(F),G=reverse(G);
    	for(int i=0;i<lim2;++i) NTT(F[i],0),NTT(G[i],0);
    	for(int i=0;i<lim2;++i)for(int j=0;j<lim1;++j)
    		F[i][j]=mul(F[i][j],G[i][j]);
    	for(int i=0;i<lim2;++i) NTT(F[i],1),NTT(G[i],1);
    	F=reverse(F),G=reverse(G);
    	for(int i=0;i<lim1;++i) NTT(F[i],1),NTT(G[i],1);
    	F.resize(n1+m1+1);
    	for(int i=0;i<=n1+m1;++i) F[i].resize(n2+m2+1);
    	return F;
    }
    
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    	}
    	int n=read<int>(),P=read<int>(),m=read<int>();
    	poly2 F(P,poly(m+1)),G(P,poly(m+1));
    	F[0][0]=1;
    	for(int i=0;i<=min(9,m);++i) G[i%P][i]=1;
    	int power=10%P;
    	for(;n;n>>=1){
    		function<poly2(poly2,poly2)> merge=[&](CO poly2&F,CO poly2&G)->poly2{
    			poly2 H(P,poly(m+1));
    			for(int i=0;i<=P-1;++i) H[i*power%P]=H[i*power%P]+F[i];
    			H=H*G;
    			for(int i=0;i<=2*P-2;++i) H[i].resize(m+1);
    			for(int i=P;i<=2*P-2;++i) H[i%P]=H[i%P]+H[i];
    			H.resize(P);
    			return H;
    		};
    		if(n&1) F=merge(F,G);
    		G=merge(G,G);
    		power=power*power%P;
    	}
    	for(int i=1;i<=m;++i) F[0][i]=add(F[0][i-1],F[0][i]);
    	for(int i=0;i<=m;++i) printf("%d%c",F[0][i]," 
    "[i==m]);
    	return 0;
    }
    

    这题其实方法很多,下面介绍一种常数比较好的做法:

    不同的位对数位和的贡献都是等价的,区别在于它们模(p)的值。然而实际上不等价的只有(p)类。

    预处理出( ext{num}_i)表示模(p = i)(10^x)种数。然后对每一种去做个倍增FFT,得到(f_j)表示( ext{num}_i)(0..9),和为(j)的方案数(这一步为(O(pm log m log n))( ext{num}_i)是都不同的,但是倍增过程可以共用以减小常数)。

    (i)类若和为(j),对模(p)的贡献就是(ij mod p)。然后把(p)类用个二维FFT合起来。模(p)的那维很小,直接暴力卷积就行了。(要做(p)次二维FFT,每次DFT是(O(m log m ∗ p))的,卷积是(O(mp^2))的)

    总复杂度是(O(pm log m log n + p^2m log m + p^3m))

    CO int N=1<<15;
    int omg[2][N],rev[N];
    
    void NTT(poly&F,int dir){
    	int lim=F.size(),len=log2(lim);
    	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    	for(int i=0;i<lim;++i)if(i<rev[i]) swap(F[i],F[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[dir][N/(i<<1)*k],F[j+i+k]);
    			F[j+i+k]=add(F[j+k],mod-t),F[j+k]=add(F[j+k],t);
    		}
    	if(dir){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) F[i]=mul(F[i],ilim);
    	}
    }
    poly operator*(poly F,poly G){
    	int n=F.size()+G.size()-1,lim=1<<int(ceil(log2(n)));
    	F.resize(lim),NTT(F,0);
    	G.resize(lim),NTT(G,0);
    	for(int i=0;i<lim;++i) F[i]=mul(F[i],G[i]);
    	NTT(F,1),F.resize(n);
    	return F;
    }
    
    typedef array<array<int,100>,100> matrix;
    int P,cnt[50];
    
    matrix operator*(CO matrix&A,CO matrix&B){
    	matrix C={};
    	for(int k=0;k<=2*P-1;++k)
    		for(int i=0;i<=2*P-1;++i)for(int j=0;j<=2*P-1;++j)
    			C[i][j]=add(C[i][j],mul(A[i][k],B[k][j]));
    	return C;
    }
    matrix pow(matrix B,int k){
    	matrix A={};
    	for(int i=0;i<=2*P-1;++i) A[i][i]=1;
    	for(;k;k>>=1,B=B*B)
    		if(k&1) A=A*B;
    	return A;
    }
    
    poly B[30];
    
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    	}
    	int n=read<int>();
    	read(P);
    	if(P==1) cnt[0]=n;
    	else{
    		matrix A={};
    		for(int i=0;i<=P-1;++i) ++A[i][i*10%P],++A[i][i+P],++A[i+P][i+P];
    		A=pow(A,n);
    		for(int i=0;i<=P-1;++i) cnt[i]=A[1][i+P];
    	}
    	int m=read<int>();
    	B[0].resize(m+1);
    	for(int i=0;i<=min(9,m);++i) B[0][i]=1;
    	for(int i=1;1<<i<=n;++i) B[i]=B[i-1]*B[i-1],B[i].resize(m+1);
    	poly2 F(P,poly(m+1));
    	F[0][0]=1;
    	for(int i=0;i<=P-1;++i)if(cnt[i]){
    		poly A(m+1);
    		A[0]=1;
    		for(int j=0;1<<j<=cnt[i];++j)if(cnt[i]>>j&1)
    			A=A*B[j],A.resize(m+1);
    		poly2 G(P,poly(m+1));
    		for(int j=0;j<=m;++j) G[j*i%P][j]=A[j];
    		int lim=1<<(int)ceil(log2(2*m+1));
    		for(int j=0;j<=P-1;++j){
    			F[j].resize(lim),NTT(F[j],0);
    			G[j].resize(lim),NTT(G[j],0);
    		}
    		poly2 H(P,poly(lim));
    		for(int j=0;j<=P-1;++j)for(int k=0;k<=P-1;++k)
    			for(int l=0;l<lim;++l)
    				H[(j+k)%P][l]=add(H[(j+k)%P][l],mul(F[j][l],G[k][l]));
    		for(int j=0;j<=P-1;++j)
    			NTT(H[j],1),H[j].resize(m+1),F[j]=H[j];
    	}
    	for(int i=1;i<=m;++i) F[0][i]=add(F[0][i-1],F[0][i]);
    	for(int i=0;i<=m;++i) printf("%d%c",F[0][i]," 
    "[i==m]);
    	return 0;
    }
    
  • 相关阅读:
    C++ unordered_set运用实例
    C++ Multimap运用实例—查找元素
    C++ Multimap运用实例
    C++ Map运用实例
    C++ Set运用实例
    C++ list运用实例
    C++ vector使用实例
    c++ Array运用实例
    C++ int double float对应的长度以及二进制
    引用和指针有什么区别
  • 原文地址:https://www.cnblogs.com/autoint/p/12976660.html
Copyright © 2011-2022 走看看