有趣的數列
阿新 • • 發佈:2018-11-08
題目描述
我們稱一個長度為2n的數列是有趣的,當且僅當該數列滿足以下三個條件:
(1)它是從1到2n共2n個整數的一個排列{ai};
(2)所有的奇數項滿足a1<a3<...<a2n-1,所有的偶數項滿足a2<a4<...<a2n;
(3)任意相鄰的兩項a2i-1與a2i(1<=i<=n)滿足奇數項小於偶數項,即:a2i-1<a2i。
現在的任務是:對於給定的n,請求出有多少個不同的長度為2n的有趣的數列。因為最後的答案可能很大,所以只要求輸出答案 mod P的值。
輸入輸出格式
輸入格式:
輸入檔案只包含用空格隔開的兩個整數n和P。輸入資料保證,50%的資料滿足n<=1000,100%的資料滿足n<=1000000且P<=1000000000。
輸出格式:
僅含一個整數,表示不同的長度為2n的有趣的數列個數mod P的值。
當一個1->n數列中每個數位於奇數位置還是偶數位置確定了時,這個數列就確定了,因為奇數位置要求遞增,偶數位置要求遞增
所有選擇是C(2n,n)
但有時是不符合的,比如1(奇),2(偶),3(偶),4(奇),5(偶),6(奇)
其實就是掃描1-n的序列,放於奇數位置的數的數量總是大於等於放於偶數位置的數的數量
這樣就能保證後面更大一些的數,放於偶數位置的數的數量總是大於等於放於奇數位置的數的數量,這樣就能滿足第三條
就是卡特蘭數
但是n是1e6的,這個遞推逆元就要寫高精,但是可以用唯一分解定理,在乘或者除一個數時,只用把他的所有質因數的指數加或者減
這個質因數可以線上性篩的時候記錄最大的質因數,然後不斷的除,乘
當然肯定是看題解的.....
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 typedef long long ll; 8 const ll maxn=3e6+7; 9 ll n,p,num,ans; 10 ll pri[maxn],d[maxn],cnt[maxn];11 bool isp[maxn]; 12 void gettable(){ 13 isp[1]=true; 14 for(ll i=2;i<=2*n;i++){ 15 if(!isp[i]) pri[++num]=i,d[i]=num; 16 for(ll j=1;pri[j]*i<=2*n&&j<=num;j++){ 17 isp[pri[j]*i]=true;d[pri[j]*i]=j; 18 if(i%pri[j]==0) break; 19 } 20 } 21 } 22 void add(ll x,ll val){ 23 while(x!=1){ 24 cnt[d[x]]+=val; 25 x/=pri[d[x]]; 26 } 27 }//cnt是最大質因數在質數表中的編號 28 int main(){ 29 cin>>n>>p; 30 gettable();ans=1; 31 for(ll i=n+2;i<=2*n;i++) add(i,1); 32 for(ll i=1;i<=n;i++) add(i,-1); 33 for(ll i=1;i<=num;i++){ 34 if(cnt[i]>0){ 35 while(cnt[i]--) ans=(ans*pri[i])%p; 36 } 37 else if(cnt[i]<0){ 38 while(cnt[i]++) ans=(ans/pri[i])%p; 39 } 40 } 41 cout<<ans<<endl; 42 }