1. 程式人生 > >有趣的數列

有趣的數列

題目描述

我們稱一個長度為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 }