[SDOI2017]序列计数

Posted fhq_treap

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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;
}

以上是关于[SDOI2017]序列计数的主要内容,如果未能解决你的问题,请参考以下文章

codevs 5964 [SDOI2017]序列计数

[bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

LibreOJ #2002. 「SDOI2017」序列计数

[Sdoi2017]序列计数

BZOJ4818: [Sdoi2017]序列计数

[SDOI2017]序列计数