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 }
View Code

Review

比较容易的一道题

posted @ 2018-07-21 23:39  wawawa8  阅读(131)  评论(0编辑  收藏  举报