1. 程式人生 > >[BZOJ4818][SDOI2017]序列計數

[BZOJ4818][SDOI2017]序列計數

序列 cst alice += 要求 php pri sample scan

bzoj
luogu

Description

Alice想要得到一個長度為\(n\)的序列,序列中的數都是不超過\(m\)的正整數,而且這\(n\)個數的和是\(p\)的倍數。Alice還希望,這\(n\)個數中,至少有一個數是質數。Alice想知道,有多少個序列滿足她的要求。

Input

一行三個數,\(n,m,p\)
\(1\le n\le 10^9,1\le m\le 2*10^7,1\le p\le 100\)

Output

一行一個數,滿足Alice的要求的序列數量,答案對\(20170408\)取模。

Sample Input

3 5 3

Sample Output

33

sol

這算是\(SDOI\)

簽到題?
首先至少有一個質數的方案數=總方案數-一個質數都沒有的方案數。
所以篩出\(m\)以內的質數做兩遍就好了。
註意到原\(dp\)式是一個卷積的形式,所以可以\(O(p^2)\)直接計算(你要寫\(MTT\)沒人攔你)
所以就做完了,復雜度\(O(m+p^2\log n)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2e7+5;
const int mod = 20170408;
const int P = 105
; bool zhi[N]; int pri[N],tot,n,m,p,f[P],g[P],ans; void mul(int *a,int *b){ int tmp[P];memset(tmp,0,sizeof(tmp)); for (int i=0;i<p;++i) for (int j=0;j<p;++j) (tmp[(i+j)%p]+=1ll*a[i]*b[j]%mod)%=mod; for (int i=0;i<p;++i) a[i]=tmp[i]; } int fastpow(int *a,int *b,int
n){ for (;n;n>>=1,mul(b,b)) if (n&1) mul(a,b); return a[0]; } int main(){ scanf("%d%d%d",&n,&m,&p); zhi[1]=true; for (int i=2;i<=m;++i){ if (!zhi[i]) pri[++tot]=i; for (int j=1;j<=tot&&i*pri[j]<=m;++j){ zhi[i*pri[j]]=1; if (i%pri[j]==0) break; } } for (int i=1;i<=m;++i) ++g[i%p]; f[0]=1;ans=fastpow(f,g,n); memset(f,0,sizeof(f));memset(g,0,sizeof(g)); for (int i=1;i<=m;++i) if (zhi[i]) ++g[i%p]; f[0]=1;ans=(ans-fastpow(f,g,n)+mod)%mod; printf("%d\n",ans);return 0; }

[BZOJ4818][SDOI2017]序列計數