zoukankan      html  css  js  c++  java
  • BZOJ4818: [Sdoi2017]序列计数

    BZOJ4818: [Sdoi2017]序列计数

    Description

    Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。
    Alice还希望,这n个数中,至少有一个数是质数。
    Alice想知道,有多少个序列满足她的要求。

    Input

    一行三个数,n,m,p。
    1<=n<=10^9,1<=m<=2×10^7,1<=p<=100

    Output

    一行一个数,满足Alice的要求的序列数量,答案对20170408取模。

    Sample Input

    3 5 3

    Sample Output

    33
    题解Here!

    据说正解是DP+矩阵快速幂。。。

    然而我用了生成函数+快速幂,FFT都没用到。。。

    首先这题显然补集转化,就是用全部方案减去不含任何质数的方案。

    考虑m比较小,我们能大力把<=m的质数全都筛出来。

    发现n很大,要么倍增要么快速幂。。。

    发现p相当小,所以我们能在mod p的同余系下做啊。

    一看到同余系下求方案数立刻想到卷积和生成函数。。。

    假设我们有一个多项式f(x),其中 x的系数为 a个数的序列之和 mod p为 i 的方案数(a为我们引入的变量)。

    同时我们有另一个多项式g(x),其中 x的系数为b个数的序列之和 mod p为 i 的方案数(b为我们引入的变量)。

    那么,我们如果让f(x)和g(x)做卷积的话,新的多项式 x的系数就是(a+b)个数的序列之和 mod p为 i 的方案数。

    这就是生成函数了。

    回到这个题,我们先初始化多项式f(x),令 x的系数为 1个数的序列之和 mod p为 i 的方案数。

    然后我们求出这个多项式的n次方,就是我们需要的答案了。

    发现这道题的p很小,我们连FFT都不用,直接用一个多项式类暴力快速幂就行了。复杂度O(m+p2*log2n),跑的飞快。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define MAXN 110
    #define MAXM 2000010
    #define MOD 20170408
    using namespace std;
    int n,m,p,k=0,prime[MAXM];
    bool np[MAXM*10];
    struct node{
    	long long num[MAXN];
    	node(){memset(num,0,sizeof(num));}
    }one,two;
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    void make(){
    	np[0]=np[1]=true;
    	for(int i=2;i<=m;i++){
    		if(!np[i])prime[++k]=i;
    		for(int j=1;j<=k&&(long long)prime[j]*i<=m;j++){
    			np[prime[j]*i]=true;
    			if(i%prime[j]==0)break;
    		}
    	}
    	for(int i=1;i<=m;i++){
    		one.num[i%p]++;
    		if(np[i])two.num[i%p]++;
    	}
    }
    node operator *(const node &x,const node &y){
    	node ret;
    	for(int i=0;i<p;i++)
    	for(int j=0;j<p;j++)
    	ret.num[(i+j)%p]=(ret.num[(i+j)%p]%MOD+x.num[i]%MOD*y.num[j]%MOD)%MOD;
    	return ret;
    }
    node mexp(node base,int k){
    	node ans=base;
    	k--;
    	while(k){
    		if(k&1)ans=ans*base;
    		base=base*base;
    		k>>=1;
    	}
    	return ans;
    }
    int main(){
    	n=read();m=read();p=read();
    	make();
    	one=mexp(one,n);
    	two=mexp(two,n);
    	long long ans=(one.num[0]-two.num[0]+MOD)%MOD;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    C++函数模板的显示调用与隐式调用
    git显示颜色配置
    STL容器元素应满足的条件
    vector缩减容量
    PAT (Basic Level) Practise:1036. 跟奥巴马一起编程
    Core Java Volume I — 4.10. Class Design Hints
    Core Java Volume I — 4.7. Packages
    蓝牙(Profile)构成
    Android开发之Java必备基础
    主机控制器接口(HCI)
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9097330.html
Copyright © 2011-2022 走看看