您的位置:首页 > 其它

BZOJ 3992: [SDOI2015]序列统计 倍增dp 原根 NTT

2018-02-23 10:44 543 查看

3992: [SDOI2015]序列统计

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 1781  Solved: 842
[Submit][Status][Discuss]

Description

小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。

Input

一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。1<=N<=10^9,3<=M<=8000,M为质数0<=x<=M-1,输入数据保证集合S中元素不重复

Output

一行,一个整数,表示你求出的种类数mod 1004535809的值。

Sample Input

4 3 1 2
1 2

Sample Output

8
【样例说明】
可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、
(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)
应该可以讲一眼秒掉吧。
一道疯狂拼接的组合套路题。
看到乘积取模 M 又是质数 直接想原根指标
之后 前两天刚做了两道 “倍增dp"(应该可以这么叫吧)
转移是卷积形式 NTT 优化一下就好了

不过 x==0 的时候 是不是得特判搞啊... 直接这样 为啥就过了...
LOJ上范围是 1<=x<=m-1 啊...

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

typedef long long ll;

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=10*x+ch-'0';ch=getchar();}
return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=25010,mod=1004535809;

int n,M,X,S,G,lim;

inline int qpow(int x,int y,int P)
{
int res(1);
while(y)
{
if(y&1) res=1ll*res*x%P;
x=1ll*x*x%P;
y>>=1;
}
return res;
}

int I
,to
;

void get_g()
{
vector<int> vec;
register int i,tmp(M-1);
for(i=2;i<tmp;++i)
{
if(tmp%i==0)
{
vec.push_back(i);
while(tmp%i==0) tmp/=i;
}
if(tmp==1) break;
}
if(tmp>1) vec.push_back(tmp);
tmp=M-1;G=1;
while(1)
{
G++;
bool flag(0);
for(i=0;i<vec.size();++i)
if(qpow(G,tmp/vec[i],M)==1)
{flag=1;break;}
if(!flag) break;
}
for(i=1,tmp=G;i<M-1;++i,tmp=tmp*G%M) I[tmp]=i;
}

int r
;

void ntt(int *x,int opt)
{
register int i,j,k,m,gn,g,tmp;
for(i=0;i<lim;++i)
if(r[i]<i)
swap(x[i],x[r[i]]);
for(m=2;m<=lim;m<<=1)
{
k=m>>1;
gn=qpow(3,(mod-1)/m,mod);
for(i=0;i<lim;i+=m)
{
g=1;
for(j=0;j<k;++j,g=1ll*g*gn%mod)
{
tmp=1ll*x[i+j+k]*g%mod;
x[i+j+k]=(x[i+j]-tmp+mod)%mod;
x[i+j]=(x[i+j]+tmp)%mod;
}
}
}
if(opt==-1)
{
reverse(x+1,x+lim);
register int inv=qpow(lim,mod-2,mod);
for(i=0;i<lim;++i)
x[i]=1ll*x[i]*inv%mod;
}
}

int f
,buc
,B
;

void mul(int *A,int *b)
{
register int i;
memcpy(B,b,sizeof(B));
ntt(A,1);ntt(B,1);
for(i=0;i<lim;++i)
A[i]=1ll*A[i]*B[i]%mod;
ntt(A,-1);
for(i=M-1;i<(M<<1)-1;++i)
(A[i%(M-1)]+=A[i])%=mod,A[i]=0;
}

void qpow()
{
while(n)
{
if(n&1) mul(f,buc);
mul(buc,buc);
n>>=1;
}
}

int main()
{
n=read();M=read();X=read();S=read();
get_g();
register int i,x;
while(S--)
{
x=read()%M;
if(x) buc[I[x]]++;
}
f[0]=1;lim=1;
while(lim<(M<<1)) lim<<=1;
for(i=0;i<lim;++i)
r[i]=(i&1)*(lim>>1)+(r[i>>1]>>1);
qpow();
cout<<f[I[X]]<<endl;
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: