bzoj3992
先暴力推了一波dp式子,看上去有点像卷积,可是第i项是和i/j项有关的。
由于m是质数,所以可以尝试用原根,因为原根gi(0~p-2)在模p意义下两两不同并且不为0,所以对一个数x,若x=gi mod (m - 1),那么就可以用i代替x,这样就可以把问题转换为在一个数集中选取n个数它们的和在模(p-1)意义下的值是x,这样dp中第i项就是和第i-j项有关的。
原根的求法,对于一个质数p,对p-1分解质因数p=p1q1*p2q2...pnqn,然后我们从2开始枚举,如果i((p-1)/pi)均不为1,那么i就是p的一个原根,因为原根不大所以这样的方法是没有问题的。
但是有一点要注意,这题中输入的数可能为0,这时候应该直接无视掉,原根无法处理这样的情况。
f[i][j]代表选i个数,数的和在模(m-1)意义下为j的方案数,那么f[i][j]=f[i-1][(j-num[i]+m-1) mod (m-1)]然后这就是个循环卷积,我们对长度为2m的多项式做多项式乘法,然后大于等于m-1的项累加到i mod (m-1)的项上,并清0。
可是那个n很大,我们发现我们只要计算出f[1],最后的多项式就是f[1]^n,快速幂就ok了
#include<bits/stdc++.h>
using namespace std;
#define maxn 200000
const int mo=1004535809;
#define ll long long
int ksm(int a,int b,int c){int res=1;for(;b;b>>=1,a=1LL*a*a%c)if(b&1)res=1LL*res*a%c;return res;}
int n,invn,nn,R[maxn],p,root,x,m;
void NTT(int *a,int f){
int id=0;
for(int i=1;i<nn;i++)if(i<R[i])swap(a[i],a[R[i]]);
for(int i=1;i<nn;i<<=1){
id++;
int wn=ksm(3,f==1?(mo-1)/(1<<id):mo-1-(mo-1)/(1<<id),mo);
for(int j=0;j<nn;j+=(i<<1)){
int w=1;
for(int k=0;k<i;k++){
int x=a[j+k],y=(1LL*w*a[j+k+i])%mo;
a[j+k]=1LL*(x+y)%mo,
a[j+k+i]=(1LL*(x-y)%mo+mo)%mo,
w=(1LL*w*wn)%mo;
}
}
}
if(f==-1)for(int i=0;i<nn;i++)a[i]=1LL*a[i]*invn%mo;
}
int T[maxn],mm;
int aa[maxn],bb[maxn],cc[maxn],dd[maxn],pos[maxn];
inline bool judge(int x, int p){
for (int i=2;i*i<=p;i++)
if ((p-1)%i==0&&ksm(x,(p-1)/i,p)==1)return 0;
return 1;
}
inline int Find_Root(int p){
if(p==2)return 1;
int res = 2;
for(;!judge(res,p);res++);
return res;
}
int num[maxn];
void init(){
scanf("%d%d%d%d",&n,&p,&x,&m);
for(int i=1;i<=m;i++)scanf("%d",&T[i]);
mm=p*2-2;
int l=0;for(mm=p*2-2,nn=1;nn<=mm;nn<<=1)l++;
for(int i=1;i<nn;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(l-1));
invn=ksm(nn,mo-2,mo);
root=Find_Root(p);
for(int i=0;i<p-1;i++)num[i]=!i?1:num[i-1]*root%p,pos[num[i]]=i;
}
void mul(int *ret,int *num1,int *num2){
for(int i=0;i<nn;i++)cc[i]=num1[i],dd[i]=num2[i];
NTT(cc,1);NTT(dd,1);
for(int i=0;i<nn;i++)ret[i]=(1LL*cc[i]*dd[i])%mo;
NTT(ret,-1);
for(int i=nn-1;i>=p-1;i--){
ret[i-p+1]=1LL*(ret[i-p+1]+ret[i])%mo;
ret[i]=0;
}
}
void quickpow2(int *a,int b){
aa[0]=1;for(;b;b>>=1,mul(a,a,a))if(b&1)mul(aa,a,aa);
}
void solve(){
for(int i=1;i<=m;i++){
if(T[i]==0)continue;
bb[pos[T[i]]]++;
}
quickpow2(bb,n);
int ans=aa[pos[x]];
printf("%d\n",ans);
}
int main(){
init();
solve();
return 0;
}

浙公网安备 33010602011771号