Loading

P3702 [SDOI2017]序列计数

题意

求长度为 \(n\) 的元素是不超过 \(m\) 的正整数的序列,其和是 \(p\) 的倍数并且元素中有至少一个质数的方案数。

Solution

暴力 OGF 好题!

考虑 \(p\) 比较小,容易想到在模 \(p\) 的剩余系下做。然后看到至少一个质数,容易想到正难则反。那问题转化成给定一个集合,然后求集合中元素组成的序列和在 \(p\) 的剩余系下是 \(0\) 的方案数。

然后直接掏出一个 OGF:\([x^n]f\) 表示集合中有多少个数在 \(p\) 的剩余系下是 \(n\)。那么 \(f^2\) 就表示长度为 \(2\) 的序列中和的生成函数。那么我们求 \(f^n\) 就得到长度为 \(n\) 的序列的和的剩余系的 OGF 了。

由于 \(f\) 大小不超过 \(p\),写一个剩余系下的乘法,然后套一个快速幂就可以了。复杂度 \(O(m+p^2\log n)\)

不要开 long long!!!

Code

using namespace std;
const int MAXN=2e7+10;
const int MOD=20170408;
int P;
struct Poly{int f[110];};
Poly mul(Poly a,Poly b){
	Poly tmp;
	memset(tmp.f,0,sizeof(tmp.f));
	rep(i,0,P-1) rep(j,0,P-1)
		(tmp.f[(i+j)%P]+=1ll*a.f[i]*b.f[j]%MOD)%=MOD;
	return tmp;
}
int p[MAXN];
vector<int> pr;
Poly F,G,Rf,Rg;
void init(int m){
	p[1]=1;
	rep(i,2,m){
		if(!p[i]) pr.pb(i);
		for(int j=0;j<(int)pr.size()&&i*pr[j]<=m;j++){
			p[i*pr[j]]=1;
			if(i%pr[j]==0) break;
		}
	}rep(i,1,m){
		F.f[i%P]++;
		if(p[i]) G.f[i%P]++;
	}
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n,m;cin>>n>>m>>P;
	init(m);
	memset(Rf.f,0,sizeof(Rf.f));Rf.f[0]=1;
	memset(Rg.f,0,sizeof(Rg.f));Rg.f[0]=1;
	while(n){
		if(n&1) Rf=mul(Rf,F),Rg=mul(Rg,G);
		F=mul(F,F);G=mul(G,G);n>>=1;
	}
	cout<<((Rf.f[0]-Rg.f[0])%MOD+MOD)%MOD<<'\n';
	return 0;
}
posted @ 2022-07-26 10:42  ZCETHAN  阅读(20)  评论(0编辑  收藏  举报