1. 程式人生 > 實用技巧 >LOJ #6059. 「2017 山東一輪集訓 Day1」Sum

LOJ #6059. 「2017 山東一輪集訓 Day1」Sum

陳指導秒掉的題,不過確實好像挺顯然的說

首先假設我們眼瞎沒看見\(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;
}