[SDOI2017]序列计数

序列计数

Alice想要得到一个长度为(n)的序列,序列中的数都是不超过(m)的正整数,而且这(n)个数的和是(p)的倍数。

Alice还希望,这(n)个数中,至少有一个数是质数。

Alice想知道,有多少个序列满足她的要求。答案对(20170408)取模。

(100\%)的数据,(1leq n leq 10^9,1leq m leq 2 imes 10^7,1leq pleq 100)

题解

至少有一个是质数不好做,用所有方案-没有质数的方案。

生成函数就是在(mod p)意义下的统计。然后将它(n)次方即可。

用线性筛去掉质数。暴力快速幂+卷积即可。

时间复杂度(O(m+p^2log n))

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read(){
	T x=0,w=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*w;
}
template<class T>il T read(T&x){
	return x=read<T>();
}
using namespace std;
typedef long long LL;

co int mod=20170408;
il int add(int a,int b){
	return (a+=b)>=mod?a-mod:a;
}
il int mul(int a,int b){
	return (LL)a*b%mod;
}

co int P=100;
int n,m,p;
int f[P],g[P],ans;

void mul_to(int f[],int g[],int h[]){
	static int a[P],b[P];
	for(int i=0;i<p;++i) a[i]=f[i],b[i]=g[i];
	for(int i=0;i<p;++i) h[i]=0;
	for(int i=0;i<p;++i)
		for(int j=0;j<p;++j)
			h[(i+j)%p]=add(h[(i+j)%p],mul(a[i],b[j]));
}

co int N=20000000+1;
int vis[N],pri[N],pc;
int main(){
	read(n),read(m),read(p);
	for(int i=1;i<=m;++i) ++g[i%p];
	f[0]=1;
	for(int i=n;i;i>>=1,mul_to(g,g,g))
		if(i&1) mul_to(f,g,f);
	ans=f[0];
	vis[1]=1;
	for(int i=2;i<=m;++i){
		if(!vis[i]) pri[++pc]=i;
		for(int j=1;j<=pc&&i*pri[j]<=m;++j){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0) break;
		}
	}
	for(int i=0;i<p;++i) f[i]=g[i]=0;
	for(int i=1;i<=m;++i)if(vis[i]) ++g[i%p];
	f[0]=1;
	for(int i=n;i;i>>=1,mul_to(g,g,g))
		if(i&1) mul_to(f,g,f);
	ans=add(ans,mod-f[0]);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/11311485.html