LOJ #6059. 「2017 山東一輪集訓 Day1」Sum
阿新 • • 發佈:2020-07-25
陳指導秒掉的題,不過確實好像挺顯然的說
首先假設我們眼瞎沒看見\(n\le 10^9\),顯然就是一個數位DP,設\(f_{i,j,k}\)表示做了\(i\)位,\(i\)位的值模\(p\)為\(j\),每位之和為\(k\)的方案數,轉移列舉填哪個數即可
然後現在我們發現\(n\)很大,因此我們套路地選擇倍增,只要考慮兩種情況
- \(f_i\to f_{i+1}\),同上,直接列舉即可
- \(f_i\to f_{2i}\),稍微推一下轉移方程為\(f_{2i,(j+k)\operatorname{mod} p,u+v}=\sum_{u,v} f_{i,j,u}\times f_{i,k,v}\)
顯然後面的那個轉移形式是個卷積,用NTT維護下即可
注意一個坑點,後四個點\(p\le 16\),但是前面的點\(p\le 50\),因此我和陳指導都傻乎乎的RE了一發
#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=1005,mod=998244353; int n,p,m,f[50][N],g[50][N],p10,ans; inline int quick_pow(int x,int p=mod-2,int mul=1) { for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul; } namespace Poly { int rev[N<<2],A[N<<2],B[N<<2],lim,p; inline void NTT(int* f,CI opt) { RI i,j,k; for (i=0;i<lim;++i) if (i<rev[i]) swap(f[i],f[rev[i]]); for (i=1;i<lim;i<<=1) { int D=quick_pow(3,opt==1?(mod-1)/(i<<1):mod-1-(mod-1)/(i<<1)),W; for (j=0;j<lim;j+=(i<<1)) for (W=1,k=0;k<i;++k,W=1LL*W*D%mod) { int x=f[j+k],y=1LL*f[i+j+k]*W%mod; f[j+k]=(x+y)%mod; f[i+j+k]=(x-y+mod)%mod; } } if (!~opt) { int Inv=quick_pow(lim); for (i=0;i<lim;++i) f[i]=1LL*f[i]*Inv%mod; } } inline void init(CI n) { for (lim=1,p=0;lim<=(m<<1);lim<<=1,++p); for (RI i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<p-1); } inline void Convolution(int* a,int* b,int* c) { RI i; for (i=0;i<lim;++i) A[i]=B[i]=0; for (i=0;i<=m;++i) A[i]=a[i],B[i]=b[i]; for (NTT(A,1),NTT(B,1),i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod; for (NTT(A,-1),i=0;i<=m;++i) (c[i]+=A[i])%=mod; } }; inline void solve(CI n) { if (!n) return (void)(f[0][0]=p10=1); solve(n>>1); RI i,j,k; for (i=0;i<p;++i) for (j=0;j<=m;++j) g[i][j]=0; for (i=0;i<p;++i) for (j=0;j<p;++j) Poly::Convolution(f[i],f[j],g[(i*p10+j)%p]); for (i=0;i<p;++i) for (j=0;j<=m;++j) f[i][j]=g[i][j]; if (n&1) { for (i=0;i<p;++i) for (j=0;j<=m;++j) g[i][j]=0; for (i=0;i<p;++i) for (j=0;j<=m;++j) for (k=0;k<=9&&j+k<=m;++k) (g[(i*10+k)%p][j+k]+=f[i][j])%=mod; for (i=0;i<p;++i) for (j=0;j<=m;++j) f[i][j]=g[i][j]; } p10=p10*p10%p; if (n&1) (p10*=10)%=p; } int main() { scanf("%d%d%d",&n,&p,&m); Poly::init(m); solve(n); for (RI i=0;i<=m;++i) (ans+=f[0][i])%=mod,printf("%d ",ans); return 0; }