[SDOI2015] 序列統計
阿新 • • 發佈:2018-01-24
它的 () 有一個 use 種類 快速冪 line 約數 pri
Time Limit: 30 Sec Memory Limit: 128 MB
Submit: 1681 Solved: 780
[Submit][Status][Discuss]
1 2
可以生成的滿足要求的不同的數列有(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)。
【數據規模和約定】
對於10%的數據,1<=N<=1000;
對於30%的數據,3<=M<=100;
對於60%的數據,3<=M<=800;
對於全部的數據,1<=N<=109,3<=M<=8000,M為質數,1<=x<=M-1,輸入數據保證集合S中元素不重復
3992: [SDOI2015]序列統計
Submit: 1681 Solved: 780
[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中的所有元素。
Output
一行,一個整數,表示你求出的種類數mod 1004535809的值。
Sample Input
4 3 1 21 2
Sample Output
8HINT
【樣例說明】
可以生成的滿足要求的不同的數列有(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)。
【數據規模和約定】
對於10%的數據,1<=N<=1000;
對於30%的數據,3<=M<=100;
對於60%的數據,3<=M<=800;
對於全部的數據,1<=N<=109,3<=M<=8000,M為質數,1<=x<=M-1,輸入數據保證集合S中元素不重復
Source
Round 1 感謝yts1999上傳
先隨便找出M的一個原根 (原根很多,對於一個模數M原根大致有phi(phi(M))個),然後把S集合中的數都轉化成指標。
(順便科普一下如何判斷一個數是不是原根,因為原根要遍歷1-M-1的同余系下的所有元素,所以原根在M-1次方之前
是不能等於1的。然而不用一個次方一個判斷,直接判斷在M-1的約數除是否有1就行了(其實還可以更優化,即只判斷/某個質因子之後的約數
就行了,但是這個方法本題已經夠用了))
這樣之後mod M同余系下兩個數相乘就相當於它們的指標相加。
你問我這個有什麽用??
轉化成加法之後就可以卷積了啊,不轉化的話只能O(N^2)算。
然後這裏有一個超級超級超級超級超級超級大的大坑是集合S中的元素是非負整數!!!
不是正整數是非負整數!!!
如果有一個元素是0的話那它是沒有意義的(0沒有指標啊,然而一開始沒有特判讓idx==0的強行加了1hhhh)
轉化之後構造一個關於指標的元多項式,它的N次方的idx[x]次項的系數就是答案。
可以用求快速冪類似的方法倍增求解,不過每一次乘完了之後要維護一下次數>=M-1的(因為指標的值都是<=M-2的),
把它們的值加到 mod(M-1)同余系下的<M-1的次數項上 (如果你問為什麽不是 mod M同余系下那麽我建議你去學一下歐拉定理)
然後幾乎就是個NTT的板了。
/************************************************************** Problem: 3992 User: JYYHH Language: C++ Result: Accepted Time:4656 ms Memory:2116 kb ****************************************************************/ #include<bits/stdc++.h> #define ll long long #define mod 1004535809 #define maxn 30005 using namespace std; const int pi=3,ni=mod/pi+1; int di[maxn],tot,N,M,X,num,inv; int a[maxn],p,b[maxn],ind[maxn]; int r[maxn],e[maxn],l,n,ans[maxn]; inline int add(int x,int y,const int ha){ x+=y; if(x>=ha) x-=ha; return x; } inline int dec(int x,int y,const int ha){ x-=y; if(x<0) x+=ha; return x; } inline int mul(int x,int y,const int ha){ return (ll)x*y%ha; } inline int ksm(int x,int y,int ha){ int an=1; for(;y;y>>=1,x=(ll)x*x%ha) if(y&1) an=(ll)an*x%ha; return an; } inline bool can(int x){ for(int i=1;i<=tot;i++) if(ksm(x,di[i],M)==1) return 0; return 1; } inline void get(){ for(int i=2;i<M;i++) if(can(i)){ p=i; break; } } inline void prework(){ ind[1]=0; for(int i=1,j=p;j!=1;i++,j=(ll)j*p%M){ ind[j]=i; } } inline void NTT(int *c,int f){ for(int i=0;i<n;i++) if(i<r[i]) swap(c[i],c[r[i]]); for(int i=1;i<n;i<<=1){ int omega=(f==1?ksm(pi,(mod-1)/(i<<1),mod):ksm(ni,(mod-1)/(i<<1),mod)); for(int j=0,q=i<<1;j<n;j+=q){ int now=1; for(int k=0;k<i;k++,now=mul(now,omega,mod)){ int x=c[k+j],y=mul(now,c[k+j+i],mod); c[k+j]=add(x,y,mod); c[k+j+i]=dec(x,y,mod); } } } if(f!=1) for(int i=0;i<n;i++) c[i]=mul(c[i],inv,mod); } inline void calc(int *x,int *y){ NTT(x,1),NTT(y,1); for(int i=0;i<n;i++) x[i]=mul(x[i],y[i],mod); NTT(x,-1); int D=M-1,to; for(int i=0;i<D;i++){ to=i+D; x[i]=add(x[i],x[to],mod); } for(int i=D;i<n;i++) x[i]=0; } inline void solve(){ for(n=1,l=0;n<=((M-2)<<1);n<<=1) l++; for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); inv=ksm(n,mod-2,mod); ans[0]=1; while(N){ if(N&1){ for(int i=0;i<n;i++) e[i]=b[i]; calc(ans,e); } for(int i=0;i<n;i++) e[i]=b[i]; calc(b,e); N>>=1; } } int main(){ scanf("%d%d%d%d",&N,&M,&X,&num); M--; for(int i=2;i*i<=M;i++) if(!(M%i)){ di[++tot]=i; if(i*i!=M) di[++tot]=M/i; } M++; for(int i=1;i<=num;i++) scanf("%d",a+i); get(); prework(); for(int i=1;i<=num;i++) if(a[i]) b[ind[a[i]]]=1; solve(); printf("%d\n",ans[ind[X]]); return 0; }
[SDOI2015] 序列統計