卡特蘭數 題解

bsoj7107 ,來源不明。php

卡特蘭數ios

題目描述c++

今天,接觸信息學不久的小A剛剛學習了卡特蘭數。算法

卡特蘭數的一個經典定義是,將 \(n​\) 個數依次入棧,合法的出棧序列個數。bash

小A以爲這樣的狀況太平凡了。因而,他給出了 \(m\) 組限制,每一個限制形如 \((f_i,g_i)\) ,表示 \(f_i\) 不能在 \(g_i\) 以後出棧。學習

他想求出:在知足了這 \(m\) 組限制的前提下,共有多少個合法的出棧序列。他不喜歡大數,你只須要求出答案在模 \(998244353\) 意義下的值便可。優化

輸入格式spa

輸入第一行爲兩個非負整數 \(n\)\(m\) ,含義題面已給出。code

接下來 \(m​\) 行,每行兩個正整數, \((f_i,g_i)​\) 表示一組限制。get

輸出格式

輸出一行,爲一個非負整數,表示你求得的答案 \(\mod 998244353​\)

樣例輸入

3 1
2 3

樣例輸出

3

樣例解釋

能夠驗證 \(\{1,2,3 \}​\)\(\{2,1,3 \}​\)\(\{2,3,1 \}​\) 都是合乎條件的。

數據規模

\(n \le 300\) , \(m \le \frac{n(n-1)}{2}\) , \(f_i,g_i \le n\)

部分數據的 \(m\) 較小。

考前一天寫題解會不會漲RP

套路可是仍是感受很巧妙的轉換。關鍵在於把限制形式化的寫出來方便觀察。

考慮Catalan數的遞推公式推導方式——枚舉最後出棧的元素 \(k​\) 。其遞推式爲

\[\begin{aligned} c_0 &= 1 \\ c_n &= \sum_{k=1}^n c_{k-1} c_{n-k} \end{aligned} \]

加入限制後沒法使用排列的相對順序dp,故需改造原來的遞推。定義 \(h_{l,r}\) 爲排列 \([l,r]\) 不一樣的出棧序列方案數,若暫不考慮限制,有

\[h_{l,r} = \sum_{k=l}^r h_{l,k-1} h_{k+1,r} \]

顯然 \(c_n = h_{1,n}​\)

遞推中,出棧序列的相對順序爲 \(([l,k-1],[k+1,r],\{ k \})\) 。限制 \((f_i, g_i)\) 要求 \(f_i\)\(g_i\) 以前出棧。故知足 \(g_i \in [l,k-1], \ f_i \in [k,r]\) 或者 \(g_i \in [k+1,r], \ f_i = k\)\(k\) 都不合法,不該對 \(h_{l,r}\) 產生貢獻。故在此dp中枚舉全部限制判斷合法性便可得到 \(O(n^3 m)​\) 的算法。

這種方法看着就很naive,考慮優化合法性判斷。容易發現把 \((f_i,g_i)\) 投到二維平面上後,判斷合法性只需詢問 \((k,l) - (r,k-1)\)\((k,k+1) - (k,r)\) 兩個矩形中是否有點。二維前綴和處理便可 \(O(1)\) 判斷。因而這樣就能夠 \(O(n^3)\) 經過本題了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long ll;
ll Rd(){
	ll ans=0;bool fh=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') fh=1; c=getchar();}
	while(c>='0'&&c<='9') ans=ans*10+c-'0',c=getchar();
	if(fh) ans=-ans;
	return ans;
}
const ll MOD=998244353;
#define _ %MOD
ll PMod(ll x){
	if(x>=MOD) return x-MOD;
	else if(x<0) return x+MOD;
	else return x;
}

const ll MXN=305;
ll N,M;
ll A[MXN][MXN];
ll S[MXN][MXN];
void SpawnSum(){
	for(ll i=1;i<=N;i++)
		for(ll j=1;j<=N;j++)
			S[i][j]=S[i-1][j]+S[i][j-1]-S[i-1][j-1]+A[i][j];
}
bool Check(ll x1,ll y1,ll x2,ll y2){
	return S[x2][y2]-S[x1-1][y2]-S[x2][y1-1]+S[x1-1][y1-1];
}
ll f[MXN][MXN];
void Solve(){
	for(ll i=0;i<=N;i++) f[i+1][i]=1;
	for(ll len=1;len<=N;len++){
		for(ll l=1;l+len-1<=N;l++){
			ll r=l+len-1;
			for(ll k=l;k<=r;k++){
				if(Check(k,l,r,k-1)) continue;
				if(Check(k,k+1,k,r)) continue;
				f[l][r]=(f[l][r]+f[l][k-1]*f[k+1][r])_;
			}
		}
	}
	cout<<f[1][N];
}
int main(){
	N=Rd();M=Rd();
	for(ll i=0;i<=N;i++) for(ll j=0;j<=N;j++) A[i][j]=S[i][j]=0;
	for(ll i=1;i<=M;i++){
		ll x=Rd(),y=Rd();
		A[x][y]=1;
	}
	SpawnSum();
	Solve();
	return 0;
}

2020/12/04

相關文章
相關標籤/搜索