卧薪尝胆,厚积薄发。
SDOI2017 序列计数
Date: Mon Dec 10 14:03:23 CST 2018
In Category:
NoCategory
Description:
长度为
$n$
的序列,都是不超过
$m$
的正整数,要求和是
$p$
的倍数。至少有一个数是质数。求有多少个序列满足条件。
$1\leqslant n\leqslant 10^9,1\leqslant m\leqslant2\times 10^7,1\leqslant p\leqslant 100$
Solution:
显然是用所有的方案数减去不包含任何一个质数的方案数,第一个的生成函数就是
$[1,m]$
中
$\%p$
为这个数的个数,第二个要求必须是质数,然后把它们分别快速幂起来,用第一个
$0$
位置上的值减去第二个
$0$
位置上的值即可,由于
$p$
很小所以不用
$FFT$
。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m,p;
#define MAXM 20000010
bool isprime[MAXM];
int prime[MAXM],tot = 0;
#define MAXP 110
long long a[MAXP],b[MAXP],c[MAXP];
long long tmp[MAXP];
#define MOD 20170408
void power(long long a[],int k)
{
memset(c,0,sizeof(c));
c[0] = 1;
while(k > 0)
{
if(k & 1)
{
memset(tmp,0,sizeof(tmp));
for(int i = 0;i < p;++i)
for(int j = 0;j < p;++j)
tmp[(i + j) % p] = (tmp[(i + j) % p] + a[i] * c[j]) % MOD;
for(int i = 0;i < p;++i)c[i] = tmp[i];
}
memset(tmp,0,sizeof(tmp));
for(int i = 0;i < p;++i)
for(int j = 0;j < p;++j)
tmp[(i + j) % p] = (tmp[(i + j) % p] + a[i] * a[j]) % MOD;
for(int i = 0;i < p;++i)a[i] = tmp[i];
k = k >> 1;
}
return;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i = 2;i <= m;++i)isprime[i] = true;
for(int i = 2;i <= m;++i)
{
if(isprime[i])prime[++tot] = i;
for(int j = 1;j <= tot && i * prime[j] <= m;++j)
{
isprime[i * prime[j]] = false;
if(i % prime[j] == 0)break;
}
}
for(int i = 1;i <= m;++i)++a[i % p];
power(a,n);
for(int i = 0;i < p;++i)a[i] = c[i];
for(int i = 1;i <= m;++i)
{
if(!isprime[i])++b[i % p];
}
power(b,n);
cout << (a[0] - c[0] + MOD) % MOD << endl;
return 0;
}
In tag:
数学-组合数学-生成函数
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡