[SDOI2017]序列计数

考虑处理如下两个条件:

  1. 和为(p)的倍数
  2. 有至少一个质数

考虑正难则反的原则,至少一个质数很难算的,我们考虑求出所有满足条件一的,还有仅由合数满足条件一的个数。

(f_{i,x})为取了(i)个,(mod p = x)的方案数
那拼接一下(f_{i + j,x} = sum_{a + b = x(mod p)}f_{i,a}f_{j,b})
考虑生成函数,那么上面这个操作等于循环卷积,那么(log(n))次循环卷积就可以做了。
因为(p)太小,所以直接暴力卷。

[SDOI2017]序列计数
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 20000005
#define M 200
#define mod 20170408

ll n,m,p;
bool P[N];

ll f[200],g[200],F[200],G[200];

inline void sieve(){
	P[1] = 0;
	for(int i = 2;i <= N;++i){
		if(P[i]){
			for(int j = 2;j * i <= N;++j)
			P[i * j] = 0;
		}
	}
}

ll c[M * 2];

inline void mul(ll *a,ll *b){
	for(int i = 0;i < p;++i)
	for(int j = 0;j < p;++j)
	c[i + j] = (c[i + j] + a[i] * b[j]) % mod;
	for(int i = 0;i < p;++i)
	a[i] = (c[i] + c[i + p]) % mod,c[i] = c[i + p] = 0;
}

inline void powf(ll k){
	F[0] = 1;
	while(k){
//		for(int i = 0;i < p;++i)
//		std::cout<<f[i]<<" ";		
		if(k & 1)mul(F,f);
		mul(f,f);
		k >>= 1;
	}
}

inline void powg(ll k){
	G[0] = 1;
	while(k){
		if(k & 1)mul(G,g);
		mul(g,g);
		k >>= 1;
	}
}

int main(){
	std::memset(P,1,sizeof(P));
	scanf("%lld%lld%lld",&n,&m,&p);
	sieve();
	for(int i = 1;i <= m;++i)
	f[i % p] ++ ;
	powf(n);
	for(int i = 1;i <= m;++i)
	if(!P[i])
	g[i % p] ++ ;	
	powg(n);	
	std::cout<<(F[0] - G[0] + mod) % mod;
}
原文地址:https://www.cnblogs.com/dixiao/p/14851420.html