[bzoj3992][SDOI2015]序列統計——離散對數+NTT
阿新 • • 發佈:2019-02-04
ons name make ret bre har 復雜度 etc 題目 意義下的原根。
題目大意:
給定一個數字不超過\(m\)的集合\(S\),用\(S\)中的數生成一個長度為\(n\)的序列,求所有序列中的元素乘積模\(m\)等於\(x\)的序列的個數。
思路:
考慮最樸素的\(DP\),設\(f_{i,j}\)為選了\(i\)個數,乘積模\(m\)余\(j\)的方案數,直接轉移的時間復雜度是\(O(nm^2)\)的。
不難發現每次轉移的過程是相同的,矩陣加速顯然不太可行,考慮將乘法形式的轉移變成加法形式的轉移,這樣每次轉移即可用NTT優化。
這裏需要用到一個叫做離散對數的東西,即在取模的意義下,將每個\(m\)以內的數都表示為\(g^x\)冪的形式,這裏的\(g\)為模\(m\)
這樣我們將每個數對\(g\)取對數之後,每次轉移便可以用NTT來優化了,但是\(n\)很大還是個問題,這個時候發現多項式乘法也是滿足結合律的,既然每次的轉移多項式是一樣的,直接上快速冪即可。
/*======================================= * Author : ylsoi * Time : 2019.2.4 * Problem : bzoj3992 * E-mail : [email protected] * ====================================*/ #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("bzoj3992.in","r",stdin); freopen("bzoj3992.out","w",stdout); } template<typename T>void read(T &_){ _=0; T fl=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1; for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0'); _*=fl; } const int maxm=8000+10; const int mod=1004535809; int n,m,aim,sz,t[maxm]; bool s[maxm]; ll qpow(ll x,ll y){ ll ret=1; x%=mod; while(y){ if(y&1)ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } int lim,cnt,dn[maxm<<2]; ll g[maxm<<2],ig[maxm<<2]; void ntt(ll *A,int ty){ REP(i,0,lim-1)if(i<dn[i])swap(A[i],A[dn[i]]); for(int len=1;len<lim;len<<=1){ ll w= ty==1 ? g[len<<1] : ig[len<<1]; for(int L=0;L<lim;L+=len<<1){ ll wk=1; REP(i,L,L+len-1){ ll u=A[i],v=A[i+len]*wk%mod; A[i]=(u+v)%mod; A[i+len]=(u-v)%mod; wk=wk*w%mod; } } } if(ty==-1){ ll inv=qpow(lim,mod-2); REP(i,0,lim-1)A[i]=A[i]*inv%mod; REP(i,m-1,lim-1){ A[i%(m-1)]=(A[i%(m-1)]+A[i])%mod; A[i]=0; } } } void init(){ read(n),read(m),read(aim),read(sz); int x; REP(i,1,sz)read(x),s[x]=1; REP(i,2,m-1){ x=i; ll w=x; REP(j,1,m-2){ if(w==1){ x=-1; break; } w=w*x%m; } if(x==i)break; } for(ll i=1,j=0;j<m-1;i=i*x%m,++j) t[i]=j; lim=1,cnt=0; while(lim<=m+m)lim<<=1,++cnt; if(!cnt)cnt=1; REP(i,0,lim-1)dn[i]=dn[i>>1]>>1|((i&1)<<(cnt-1)); g[lim]=qpow(3,(mod-1)/lim); ig[lim]=qpow(g[lim],mod-2); for(int i=lim>>1;i;i>>=1){ g[i]=g[i<<1]*g[i<<1]%mod; ig[i]=ig[i<<1]*ig[i<<1]%mod; } } ll a[maxm<<2],b[maxm<<2]; void work(){ a[0]=1; REP(i,1,m-1)if(s[i])b[t[i]]=1; while(n){ ntt(b,1); if(n&1){ ntt(a,1); REP(i,0,lim-1)a[i]=a[i]*b[i]%mod; ntt(a,-1); } REP(i,0,lim-1)b[i]=b[i]*b[i]%mod; ntt(b,-1); n>>=1; } printf("%lld\n",(a[t[aim]]+mod)%mod); } int main(){ //File(); init(); work(); return 0; } ?
[bzoj3992][SDOI2015]序列統計——離散對數+NTT