1. 程式人生 > >bzoj1485 [HNOI2009]有趣的數列

bzoj1485 [HNOI2009]有趣的數列

可能 log long span 表示 put vid stdout 條件

Description

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

Input

輸入文件只包含用空格隔開的兩個整數n和P。輸入數據保證,50%的數據滿足n≤1000,100%的數據滿足n≤1000000且P≤1000000000。

Output

僅含一個整數,表示不同的長度為2n的有趣的數列個數mod P的值。

Sample Input

3 10

Sample Output

5 對應的5個有趣的數列分別為(1,2,3,4,5,6),(1,2,3,5,4,6),(1,3,2,4,5,6),(1,3,2,5,4,6),(1,4,2,5,3,6)。

正解:卡特蘭數。

打表以後發現是卡特蘭數,然後直接分解質因數求組合數就行了。

 1
#include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define N (1000010) 6 7 using namespace std; 8 9 int prime[N],num[N],n,p,cnt,phi,inv,ans; 10 11 il int qpow(RG int a,RG int b){ 12 RG int ans=1; 13 while (b){ 14 if (b&1) ans=1LL*ans*a%p;
15 a=1LL*a*a%p,b>>=1; 16 } 17 return ans; 18 } 19 20 il void divide(RG int n){ 21 phi=n; 22 for (RG int i=2;i*i<=n;++i){ 23 if (n%i) continue; 24 while (n%i==0) n/=i; 25 prime[++cnt]=i,phi=phi/i*(i-1); 26 } 27 if (n!=1) prime[++cnt]=n,phi=phi/n*(n-1); return; 28 } 29 30 il void get(RG int n,RG int v){ 31 for (RG int i=1;i<=n;++i){ 32 RG int x=i; 33 for (RG int j=1;j<=cnt;++j){ 34 if (x%prime[j]) continue; 35 while (x%prime[j]==0) num[j]+=v,x/=prime[j]; 36 } 37 if (v==1) ans=1LL*ans*x%p; 38 else inv=1LL*inv*x%p; 39 } 40 } 41 42 int main(){ 43 #ifndef ONLINE_JUDGE 44 freopen("sequence.in","r",stdin); 45 freopen("sequence.out","w",stdout); 46 #endif 47 cin>>n>>p; divide(p),ans=inv=1; 48 get(2*n,1),get(n,-1),get(n+1,-1); 49 for (RG int i=1;i<=cnt;++i) 50 ans=1LL*ans*qpow(prime[i],num[i])%p; 51 ans=1LL*ans*qpow(inv,phi-1)%p; 52 cout<<ans; return 0; 53 }

bzoj1485 [HNOI2009]有趣的數列