「luogu3702」[SDOI2017]序列计数
考虑题目中的两个限制:
- 至少有一个数是质数:简单容斥,至少有一个数是质数的方案数=总方案数-没有一个数是质数的方案数。
- 所有数的和是p 的倍数:因为p的范围很小,可以在模p意义下考虑。
然后发现这是一个dp。
dp[i][j] 表示前 i 个数的和模 p 为 j 的方案数。
发现可以用矩阵快速幂优化。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=20000010,P=110,mod=20170408; 4 int n,m,p,prime[N],tot,cnt[P],all[P]; 5 bool isp[N]; 6 void pre(){ 7 isp[1]=1,cnt[1%p]++,all[1%p]++; 8 for(int i=2;i<=m;i++){ 9 all[i%p]++; 10 if(!isp[i]){prime[++tot]=i;} 11 else cnt[i%p]++; 12 for(int j=1;j<=tot&&1LL*prime[j]*i<=m;j++){ 13 isp[i*prime[j]]=1; 14 if(!(i%prime[j])) break; 15 } 16 } 17 return; 18 } 19 struct Mat{ 20 int n,m,num[P][P]; 21 Mat(){ 22 n=m=0; 23 memset(num,0,sizeof(num)); 24 return; 25 } 26 void set1(){for(int i=1;i<=n;i++) num[i][i]=1;return;} 27 Mat operator*(const Mat& k)const{ 28 Mat c; 29 c.n=n,c.m=k.m; 30 for(int i=0;i<n;i++) 31 for(int j=0;j<k.m;j++) 32 for(int ii=0;ii<m;ii++) 33 c.num[i][j]=((long long)c.num[i][j]+(long long)num[i][ii]*k.num[ii][j])%mod; 34 return c; 35 } 36 void print(){ 37 for(int i=0;i<n;i++){ 38 for(int j=0;j<m;j++) cout<<num[i][j]<<" "; 39 cout<<endl; 40 } 41 cout<<endl; 42 return; 43 } 44 }; 45 void qpow(Mat x,int y,Mat& res){ 46 res.n=x.n,res.m=x.m; 47 res.set1(); 48 while(y){ 49 if(y&1) res=res*x; 50 x=x*x,y>>=1; 51 } 52 return; 53 } 54 55 int main(){ 56 scanf("%d%d%d",&n,&m,&p); 57 pre(); 58 Mat a,b,c,d; 59 a.n=a.m=b.n=b.m=p; 60 c.n=d.n=p,c.m=d.m=1; 61 for(int i=0;i<p;i++){ 62 for(int j=0;j<p;j++) a.num[i][j]=cnt[(i-j+p)%p],b.num[i][j]=all[(i-j+p)%p]; 63 } 64 c.num[0][0]=d.num[0][0]=1; 65 qpow(a,n-1,a);qpow(b,n-1,b); 66 c=a*c,d=b*d; 67 int ans=((d.num[0][0]-c.num[0][0])%mod+mod)%mod; 68 printf("%d",ans); 69 return 0; 70 }