[SDOI2017]序列计数
序列计数
Alice想要得到一个长度为\(n\)的序列,序列中的数都是不超过\(m\)的正整数,而且这\(n\)个数的和是\(p\)的倍数。
Alice还希望,这\(n\)个数中,至少有一个数是质数。
Alice想知道,有多少个序列满足她的要求。答案对\(20170408\)取模。
对\(100\%\)的数据,\(1\leq n \leq 10^9,1\leq m \leq 2\times 10^7,1\leq p\leq 100\)
题解
至少有一个是质数不好做,用所有方案-没有质数的方案。
生成函数就是在\(\mod p\)意义下的统计。然后将它\(n\)次方即可。
用线性筛去掉质数。暴力快速幂+卷积即可。
时间复杂度\(O(m+p^2\log 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\n",ans);
return 0;
}
静渊以有谋,疏通而知事。