bzoj 1485 卡特蘭數
連結:戳這裡
1485: [HNOI2009]有趣的數列
我們稱一個長度為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)。
題意:額 中文題的話就不需要多說了吧
思路:一開始肯定是懵逼了,畢竟我連基本的dp狀態都推不出來,然後因為是已經知道了是卡特蘭數了,所以在證明為什麼是。首先要求奇數位置升序,偶數位置升序,相鄰的奇數偶數項滿足a[i*2-1]<a[i*2]。然後我們抽象出一個模型:有2n個人排成一行進入劇場。入場費5元。其中只有n個人有一張5元鈔票,另外n人只有10元鈔票,劇院無其它鈔票,問有多少中方法使得只要有10元的人買票,售票處就有5元的鈔票找零?(將持5元者到達視作將5元入棧,持10元者到達視作使棧中某5元出棧) 這個是卡特蘭數的經典例子
我們也這樣設想一下,1~2*n順序去填序列,因為放的奇偶位置不一定要相匹配,假設當前奇數位置上填了p個,偶數位置上已經填了q個,我們是不是隻要滿足p<=q就可以了呢?想一下我們是按順序去填的序列,但是因為不一定要你一個我一個的去填,而是滿足p<=q就可以了,類似於了有一個偶數位置進棧,就有一個奇數位置出棧,類似於那種n*n推導的dp方程式,f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)
可以求出答案,但是這道題n=1e6,所以還是回到C(2*n,n)/(n+1)吧
好了我們知道了答案之後還是不能求出正確的答案,因為要取模,而mod是給定的,不一定是質數,排列組合中有除法,總所周知除法取模會出錯。那麼我們可以簡單化簡一下C(2*n,n)/(n+1)==(2*n)*(2*n-1)*...*(n+2)*(n^-1)*((n-1)^-1)*...*(1^-1) 嗯這樣是不是好看了一點,可是並沒有什麼卵用
接下來我們分析一個地方,就是怎麼簡化這個式子,因為每個數都可以變成一些質數的乘積,我們把(2*n)*(2*n-1)*...*(n+2)*(n^-1)*((n-1)^-1)*...*(1^-1)這些數全部都換成質數相乘,也就是一些質數的num次方的總乘積,質數取模的話應該是ok的,所以要模擬O(n*2)的篩選過程了,具體看程式碼吧
程式碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef unsigned long long ull;
#define INF (1ll<<60)-1
using namespace std;
int P,n;
int vis[2000100],prime[2000100],m,a[2000100],num[2000100];
void init(){
m=0;
for(int i=2;i<=n*2;i++){
if(!vis[i]) {
prime[++m]=i;
a[i]=i;
}
for(int j=1;j<=m;j++){
if(i*prime[j]>2*n) break;
vis[i*prime[j]]=1;
a[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
ll qpow(int a,int b,int mod){
ll ans=1;
while(b){
if(b%2==1) ans=ans*a%mod;
b/=2;
a=a*a%mod;
}
return ans;
}
int main(){
scanf("%d%d",&n,&P);
init();
for(int i=1;i<=n;i++) num[i]--;
for(int i=n+2;i<=n*2;i++) num[i]++;
for(int i=n*2;i>=2;i--){
if(a[i]!=i){
num[i/a[i]]+=num[i];
num[a[i]]+=num[i];
num[i]=0;
}
}
ll ans=1;
for(int i=n*2;i>=2;i--){
ans=ans*qpow(i,num[i],P)%P;
}
printf("%lld\n",ans);
return 0;
}