bzoj 4818 [Sdoi2017]序列计数
题面
https://www.lydsy.com/JudgeOnline/problem.php?id=4818
题解
显然dp
首先不考虑一定有一个质数的条件
令$f[i][j]$表示当前选择$i$个数,当前选择的数的和模$p$等于$j$的方案数
那么转移方程很好写
$$f[i+1][j]= \sum_{x=0}^{p-1} {f[i][(j-x+p) \mod p]\times sum[x]}$$
$sum[x]$ 表示模$p$等于$x$的数的个数
然后选择至少一个质数的方案数=所有的方案数-没有质数的方案数
只要改一下$sum$数组把质数都减掉再做一遍就好了
那么每一次做的时候肯定用矩阵快速幂
我们发现
这样的矩阵就可以了
f[i][0] f[i][1] ... f[i][p-1] sum[0] sum[1] ... sum[p-1]
f[i][0] f[i][1] ... f[i][p-1] * sum[p-1] sum[0] ... sum[p-2]
... ... ... .... ... ...
f[i][0] f[i][1] ... f[i][p-1] sum[1] sum[2] ... sum[0]
就能够得到
f[i+1][0] f[i+1][1] ... f[i+1][p-1]
f[i+1][0] f[i+1][1] ... f[i+1][p-1]
.... ... ...
f[i+1][0] f[i+1][1] ... f[i+1][p-1]
这样就做完了
Code
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 ll read(){ 6 ll x=0,f=1;char c=getchar(); 7 while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} 8 while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();} 9 return x*f; 10 } 11 12 const int mod=20170408; 13 14 int sum[110]; 15 int n,m,p; 16 bool isp[20000200]; 17 int pr[20000200],cnt; 18 struct Matrix{ 19 int a[110][110]; 20 Matrix(){ 21 memset(a,0,sizeof(a)); 22 for(int i=0;i<=p;i++) 23 a[i][i]=1; 24 } 25 void clear(){ 26 memset(a,0,sizeof(a)); 27 } 28 void get(){ 29 for(int i=0;i<p;i++) 30 for(int j=0;j<p;j++) 31 a[i][j]=sum[(j-i+p)%p]; 32 } 33 Matrix operator *(Matrix b){ 34 Matrix ret; 35 ret.clear(); 36 for(int i=0;i<p;i++) 37 for(int j=0;j<p;j++) 38 for(int k=0;k<p;k++) 39 ret.a[i][j]=(ret.a[i][j]+a[i][k]*1ll*b.a[k][j]%mod)%mod; 40 return ret; 41 } 42 } mat; 43 Matrix ksm(Matrix a,int t){ 44 Matrix ret; 45 while(t){ 46 if(t&1) ret=ret*a; 47 a=a*a; 48 t=t>>1; 49 } 50 return ret; 51 } 52 53 int main(){ 54 #ifdef LZT 55 freopen("in","r",stdin); 56 #endif 57 n=read(),m=read(),p=read(); 58 for(int i=0;i<p;i++) 59 sum[i]=(m-i)/p+(i<=m); 60 sum[0]--; 61 mat.get(); 62 mat=ksm(mat,n); 63 int ans=mat.a[0][0]; 64 memset(isp,1,sizeof(isp)); 65 isp[1]=0; 66 for(int i=2;i<=m;i++){ 67 if(isp[i]) pr[++cnt]=i; 68 for(int j=1;j<=cnt && i*pr[j]<=m;j++){ 69 isp[i*pr[j]]=0; 70 if(i%pr[j]==0) break; 71 } 72 } 73 for(int i=1;i<=cnt;i++) 74 sum[pr[i]%p]--; 75 mat.get(); 76 mat=ksm(mat,n); 77 ans=(ans-mat.a[0][0]+mod)%mod; 78 printf("%d\n",ans); 79 return 0; 80 }
Review
比较容易的一道题