[BZOJ4818][SDOI2017]序列计数

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;
}
posted @ 2018-05-26 12:10  租酥雨  阅读(168)  评论(0编辑  收藏  举报