[SDOI2015]序列统计
FFT优化背包
可以推出dp式子
乘法不可做。M是质数,变成原根
dp式子现在是加法
其实每次是原来的f数组,对可以转移的s集合进行卷积(即FFT优化背包)
直接快速幂搞定
详细一些:
循环卷积无非就是多了一个取值的位置,每次FFT之后,一个位置再变成两个位置的和,剩下>=m的位置再变成0
也有结合律
注意,S中有0,而0没有原根,并且x>=1所以选择0一定不是答案。直接pass掉
代码:
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') #define int long long using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=8005*8; const int mod=1004535809; int n,m,o; int len; int G; int s[N],sz; ll ret[N]; ll qm(ll x,ll y,int mo){ ll ret=1; while(y){ if(y&1) ret=ret*x%mo; x=x*x%mo; y>>=1; } return ret; } ll f[N],h[N]; int id[N]; int rev[N]; void fft(ll *f,int n,int c){ for(reg i=0;i<n;++i){ if(i<rev[i]) swap(f[i],f[rev[i]]); } for(reg p=2;p<=n;p<<=1){ int gen; if(c==1) gen=qm(3,(mod-1)/p,mod); else gen=qm(qm(3,mod-2,mod),(mod-1)/p,mod); int len=p/2; for(reg l=0;l<n;l+=p){ ll buf=1; for(reg k=l;k<l+len;++k){ ll tmp=buf*f[k+len]%mod; f[k+len]=(f[k]-tmp+mod)%mod; f[k]=(f[k]+tmp)%mod; buf=buf*gen%mod; } } } } void calc(ll *f,ll *g,int n){ // cout<<"clac nn "<<n<<endl; // cout<<" fff "<<endl; // for(reg i=0;i<n;++i){ // cout<<f[i]<<" "; // }cout<<endl; // cout<<" ggg "<<endl; // for(reg i=0;i<n;++i){ // cout<<g[i]<<" "; // }cout<<endl; for(reg i=0;i<n;++i) h[i]=g[i]; fft(f,n,1);fft(h,n,1); for(reg i=0;i<n;++i) f[i]=f[i]*h[i]%mod; fft(f,n,-1); ll inv=qm(n,mod-2,mod); for(reg i=0;i<n;++i) f[i]=f[i]*inv%mod,h[i]=f[i]; for(reg i=1;i<m;++i) { // cout<<f[i]<<" "; f[i]=(f[i]+h[i+m-1])%mod; } // cout<<endl; for(reg i=m;i<n;++i) f[i]=0; } void findG(){ for(G=2;;++G){ bool fl=true; for(reg i=1;i<=m-2;++i){ if(qm(G,i,m)==1) { fl=false;break; } } if(fl) break; } } void qsm(int y){ memset(ret,0,sizeof ret); ret[0]=1; while(y){ if(y&1) { if(ret[0]==1) memcpy(ret,f,sizeof f); else calc(ret,f,len); } calc(f,f,len); // /cout<<" ff "<<y<<endl; // for(reg i=0;i<n;++i){ // cout<<f[i]<<" "; // }cout<<endl; y>>=1; } } int main(){ rd(n);rd(m);rd(o);rd(sz); int cnt=0; int lp; for(reg i=1;i<=sz;++i){ rd(lp);if(lp) s[++cnt]=lp; } findG(); // cout<<" GG "<<G<<endl; for(reg i=1;i<=m-1;++i){ id[qm(G,i,m)]=i; } for(reg i=1;i<=cnt;++i) { s[i]=id[s[i]]; f[s[i]]=1; } for(lp=m+m,len=1;len<lp;len<<=1); for(reg i=0;i<len;++i){ rev[i]=rev[i>>1]>>1|((i&1)?len>>1:0); } // cout<<" len "<<len<<endl; qsm(n); o=id[o]; // cout<<" o "<<o<<endl; printf("%lld",ret[o]); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/1/16 21:29:34 */