• 【loj6059】Sum


    Portal --> loj6059

    Solution

    ​​  看过去第一反应是。。大力数位dp!然后看了一眼数据范围。。。

    ​  但是这没有什么关系!注意到我们不需要考虑前导零了,可以直接快乐dp

    ​  状态还是能继续用的,记(f[i][j][k])表示从左往右数的前(i)位,(假装后面没有数位的情况下)模(p)(j),数字和为(k)

    ​  然后。。(n)特别大所以我们考虑。。倍增求解,考虑从(lfloorfrac{i}{2} floor)转移到(i)

    [f[lfloorfrac{i}{2} floor][x][j]*f[lfloorfrac{i}{2} floor][y][k] ightarrow f[i][x+y][(j+10^w*k)\%p] ]

    ​  这个(w)的话就是。。(lfloorfrac{i}{2} floor)

    ​​  但是如果说(i)是奇数怎么办呢?其实只要在这样转移完了之后再暴力枚举一下最高位是啥就好了(现在是相当于得到了一个(i-1)位的数嘛)

    ​​  然后发现因为(p)(m)都比较小,所以我们可以直接枚举,而第二维的那个(f[][x][j]*f[][y][k] ightarrow f[][x+y][])的是一个卷积的形式,我们可以用NTT来优化

    ​​  具体的话其实感觉跟这题的处理有点像【Portal -->Lcm】,也是我们先将(f[i][x])DFT(NTT)一下之后就可以随便搞事了,也就是相当于第二维和第三维在某种意义上独立了,然后我们可以将转移分开处理(先搞第二维的转移,再暴力枚举第三维的转移)

    ​  至于倍增的话。。递归就好了,边界的话就是(i=0)的情况

    ​​  因为中间要快乐NTT所以一定要记得相关数组清空

    ​  

    ​​  代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int MOD=998244353,N=3010,M=3010,NT=N*4,TOP=11,G=3;
    int f[N][M],g[N][M];
    int n,m,p,ans;
    int mul(int x,int y){return 1LL*x*y%MOD;}
    int add(int x,int y){return (1LL*x+y)%MOD;}
    int ksm(int x,int y){
    	int ret=1,base=x;
    	for (;y;y>>=1,base=mul(base,base))
    		if (y&1) ret=mul(ret,base);
    	return ret;
    }
    namespace NTT{/*{{{*/
    	int A[NT],B[NT],W[NT][2],rev[NT];
    	int len,invlen,invg;
    	void get_len(int n,int m){
    		for (int i=0;i<len;++i) A[i]=B[i]=0;
    		int bit=0;
    		for (len=1;len<=n+m;len<<=1,++bit);
    		rev[0]=0;
    		for (int i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
    		invlen=ksm(len,MOD-2);
    	}
    	void init(int n){
    		invg=ksm(G,MOD-2);
    		for (int i=1;i<=TOP;++i){
    			W[1<<i][0]=ksm(G,(MOD-1)/(1<<i));
    			W[1<<i][1]=ksm(invg,(MOD-1)/(1<<i));
    		}
    		get_len(n,n);
    	}
    	void ntt(int *a,int op){
    		int w,w_n,u,v;
    		for (int i=0;i<len;++i) if (rev[i]>i) swap(a[i],a[rev[i]]);
    		for (int step=2;step<=len;step<<=1){
    			w_n=W[step][op==-1];
    			for (int st=0;st<len;st+=step){
    				w=1;
    				for (int i=0;i<(step>>1);++i){
    					v=mul(a[st+i+(step>>1)],w);
    					u=a[st+i];
    					a[st+i]=add(u,v);
    					a[st+i+(step>>1)]=add(u,MOD-v);
    					w=mul(w,w_n);
    				}
    			}
    		}
    		if (op==1) return;
    		for (int i=0;i<len;++i) a[i]=mul(a[i],invlen);
    	}
    }/*}}}*/
    int work(int n){
    	if (!n) return 1;
    	int mi=work(n>>1);
    	for (int i=0;i<p;++i) 
    		NTT::ntt(g[i],1);
    	for (int i=0;i<p;++i)
    		for (int j=0;j<p;++j)
    			for (int k=0;k<NTT::len;++k)
    				f[(i+j*mi%p)%p][k]=add(f[(i+j*mi%p)%p][k],mul(g[i][k],g[j][k]));
    	for (int i=0;i<p;++i) 
    		for (int j=0;j<NTT::len;++j)
    			g[i][j]=0;
    	for (int i=0;i<p;++i){
    		NTT::ntt(f[i],-1);
    		for (int j=0;j<m;++j) g[i][j]=f[i][j];
    		for (int j=0;j<NTT::len;++j) f[i][j]=0;
    	}
    	mi=mi*mi%p;
    	if (n&1){
    		for (int i=0;i<p;++i)
    			for (int x=0;x<10;++x)
    				for (int j=0;j+x<m;++j)
    					f[(i+x*mi%p)%p][j+x]=add(f[(i+x*mi%p)%p][j+x],g[i][j]);
    		for (int i=0;i<p;++i)
    			for (int j=0;j<m;++j)
    				g[i][j]=f[i][j],f[i][j]=0;
    		mi=mi*10%p;
    	}
    	return mi;
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d%d%d",&n,&p,&m);
    	g[0][0]=1; ++m;
    	NTT::init(m);
    	work(n);
    	ans=0;
    	for (int i=0;i<m;++i){
    		ans=add(ans,g[0][i]);
    		printf("%d ",ans);
    	}
    }
    
  • 相关阅读:
    noi2002银河英雄传说(并查集)
    Ural1076(km算法)
    km算法的个人理解
    函数之装饰器
    函数进阶(一)
    python全栈测试题(一)
    python基础之循环语句
    字符串方法总结
    python基础3
    python基础2
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9585985.html
Copyright © 2020-2023  润新知