bzoj 1485: [HNOI2009]有趣的數列 (卡特蘭數)
阿新 • • 發佈:2019-02-12
1485: [HNOI2009]有趣的數列
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1226 Solved: 652
[Submit][Status][Discuss]
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 10Sample 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)。
HINT
Source
題解:卡特蘭數
這道題打表找規律就會發現其實答案就是卡特蘭數
f[n]=c(2n,n)/n+1=c(2n,n)-c(2n,n-1)
然後分解質因數再合併,即可處理。
但是這道題,除了打表以外,還有更科學的分析方法。可以將題目中的問題轉換成卡特蘭數的經典問題——入棧出棧問題。
確定了奇數位後,偶數位要麼唯一確定,要麼不存在合法的序列。 然後就可以將奇數位看成入棧,偶數位看成出棧,轉換成卡特蘭數的模型。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 2000003 #define LL long long using namespace std; int prime[N],pd[N],num[N],mp[N]; int n,p; void init() { for (int i=2;i<=2000000;i++){ if (!pd[i]) prime[++prime[0]]=i,mp[i]=prime[0]; for (int j=1;j<=prime[0];j++){ if (prime[j]*i>2000000) break; pd[prime[j]*i]=1; if (i%prime[j]==0) break; } } } void calc(int x,int val) { int k=x; for (int i=1;prime[i]*prime[i]<=k;i++) if (k%prime[i]==0){ while (k%prime[i]==0) { k/=prime[i]; num[i]+=val; } } if (k>1) num[mp[k]]+=val; } LL quickpow(LL num,int x) { LL base=num%p; LL ans=1; while (x) { if (x&1) ans=ans*base%p; x>>=1; base=base*base%p; } return ans; } int main() { freopen("a.in","r",stdin); freopen("my.out","w",stdout); scanf("%d%d",&n,&p); init(); for (int i=n+1;i<=2*n;i++) calc(i,1); for (int i=1;i<=n;i++) calc(i,-1); calc(n+1,-1); //for (int i=1;i<=n;i++) cout<<num[i]<<" "; //cout<<endl; LL ans=1; for (int i=1;i<=prime[0];i++) ans=ans*quickpow((LL)prime[i],num[i])%p; printf("%I64d\n",ans); }