[SDOI2017]序列计数
考虑处理如下两个条件:
- 和为\(p\)的倍数
- 有至少一个质数
考虑正难则反的原则,至少一个质数很难算的,我们考虑求出所有满足条件一的,还有仅由合数满足条件一的个数。
设\(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;
}