[原根 + NTT] LOJ#2183 BZOJ3992:「SDOI2015」序列统计
2018-02-09 15:42
555 查看
做法比较显然,应该就是这样的 DPDP :
f(i)=∑j∗k≡i(modm)f′(j)g(k)f(i)=∑j∗k≡i(modm)f′(j)g(k)
可以用原根转化为加法,就变成
f(i)=∑j+k≡i(modm)f′(j)g(k)f(i)=∑j+k≡i(modm)f′(j)g(k)
是个循环卷积,循环卷积的求法就是先正常乘一次,然后把后面超出 mm 的部分加到前面去,并清0。
乘一次会算了,然后直接快速幂就行了。
f(i)=∑j∗k≡i(modm)f′(j)g(k)f(i)=∑j∗k≡i(modm)f′(j)g(k)
可以用原根转化为加法,就变成
f(i)=∑j+k≡i(modm)f′(j)g(k)f(i)=∑j+k≡i(modm)f′(j)g(k)
是个循环卷积,循环卷积的求法就是先正常乘一次,然后把后面超出 mm 的部分加到前面去,并清0。
乘一次会算了,然后直接快速幂就行了。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const LL P=1004535809ll,maxn=1000005,P_G=3,maxm=8005; inline char gc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline LL getint(){ char ch=gc(); LL res=0,ff=1; while(!isdigit(ch)) ch=='-'?ff=-1:0, ch=gc(); while(isdigit(ch)) res=(res<<3)+(res<<1)+ch-'0', ch=gc(); return res*ff; } LL Pow(LL a,LL b,LL MOD){ LL res=1; a%=MOD; for(;b;b>>=1,a=a*a%MOD) if(b&1) res=res*a%MOD; return res; } int rev[maxn]; void get_rev(int n){ int t=0; while((1<<t)<n) t++; rev[0]=0; for(int i=1;i<=n-1;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<t-1); } void NTT(LL a[],int n,int k){ for(int i=0;i<=n-1;i++) if(i<rev[i]) swap(a[i],a[rev[i]]); for(int m=2;m<=n;m<<=1){ int wm=Pow(P_G,(P-1)/m,P); if(k==-1) wm=Pow(wm,P-2,P); for(int i=0;i<=n-1;i+=m){ LL w=1,t0,t1; for(int j=0;j<=(m>>1)-1;j++,w=w*wm%P) t0=a[i+j], t1=w*a[i+j+(m>>1)]%P, a[i+j]=(t0+t1)%P, a[i+j+(m>>1)]=((t0-t1)%P+P)%P; } } if(k==-1){ int t=Pow(n,P-2,P); for(int i=0;i<=n-1;i++) a[i]=(LL)a[i]*t%P; } } int num,m,X,S,g,N; int gp[maxm],ind[maxm]; void get_mG(int m){ for(g=2;;g++){ bool pd=true; for(int j=1;j<=m-2&&pd;j++) if(Pow(g,j,m)==1) pd=false; if(pd) return; } } LL A[maxn],Ans[maxn]; void Calc(int b){ Ans[0]=1; for(;b;b>>=1){ NTT(A,N,1); if(b&1){ NTT(Ans,N,1); for(int i=0;i<=N-1;i++) Ans[i]=Ans[i]*A[i]%P; NTT(Ans,N,-1); for(int i=N-1;i>=m-1;i--) Ans[i-m+1]=(Ans[i-m+1]+Ans[i])%P, Ans[i]=0; } for(int i=0;i<=N-1;i++) A[i]=A[i]*A[i]%P; NTT(A,N,-1); for(int i=N-1;i>=m-1;i--) A[i-m+1]=(A[i-m+1]+A[i])%P, A[i]=0; } } int main(){ freopen("loj2183.in","r",stdin); freopen("loj2183.out","w",stdout); scanf("%d%d%d%d",&num,&m,&X,&S); get_mG(m); gp[0]=1; for(int i=1;i<=m-2;i++) gp[i]=gp[i-1]*g%m; for(int i=0;i<=m-2;i++) ind[gp[i]]=i; while(S--){ int x; scanf("%d",&x); if(x) A[ind[x]]=1; } N=1; while(N<m) N<<=1; N<<=1; get_rev(N); Calc(num); printf("%d\n",Ans[ind[X]]); return 0; }
相关文章推荐
- [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)
- BZOJ 3992: [SDOI2015]序列统计 倍增dp 原根 NTT
- 【XSY2119】【BZOJ3992】【SDOI2015】序列统计 原根 NTT
- [NTT 原根 指标 多项式快速幂] BZOJ 3992 [SDOI2015]序列统计
- [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)
- BZOJ 3992 [SDOI2015]序列统计 NTT
- BZOJ 3992: [SDOI2015]序列统计(NTT+快速幂+DP)
- bzoj3992 [SDOI2015]序列统计(从一道题入手NTT)
- bzoj 3992: [SDOI2015]序列统计 NTT
- BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂
- BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)
- [BZOJ3992][SDOI2015]序列统计(dp+NTT+快速幂)
- bzoj3992 [SDOI2015]序列统计(NTT快速幂)
- [BZOJ3992] [SDOI2015] [NTT] 序列统计
- 【BZOJ】3992 [SDOI2015]序列统计 【离散对数下的NTT】
- bzoj3992: [SDOI2015]序列统计 NTT+快速幂
- BZOJ[3992][SDOI2015]序列统计 生成函数+NTT
- Bzoj3992:[SDOI2015]序列统计:NTT+DP
- BZOJ 3992 SDOI 2015 序列统计 NTT 生成函数 计数 原根
- BZOJ 3992 [SDOI2015]序列统计 NTT