【2020.12.01提升組模擬】卡特蘭數(catalan)

題目

題目描述

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

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

\(A\)以爲這樣的狀況太平凡了。因而,他給出了\(m\)組限制,每一個限制形如\((f_i,g_i)\),表示\(f_i\)不能在\(g_i\)以後出棧。
他想求出:在知足了這\(m\)組限制的前提下,共有多少個合法的出棧序列。他不喜歡大數,你只須要求出答案在模\(998244353\)意義下的值便可。spa

輸入格式

輸入第一行爲兩個非負整數,\(n\)\(m\),含義題面已給出。
接下來\(m\)行,每行兩個正整數,\((f,g)\) 表示一組限制。code

輸出格式

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

樣例輸入

3 1
2 3

樣例輸出

3

樣例解釋
能夠驗證\(\{1,2,3\}\)\(\{2,1,3\}\)\(\{2,3,1\}\)都是合乎條件的。圖片

數據規模

\(編號\) \(分值\) \(n\) \(m\) \(特殊性質\)
\(1\) \(15\) \(\le 300\) \(= 0\)
\(2\) \(15\) \(\le 7\) \(\le 10\)
\(3\) \(15\) \(\le 100\) \(\le 50\)
\(4\) \(15\) \(\le 300\) \(保證全部的f_i相同\)
\(5\) \(20\) \(\le 300\) \(\le 300\)
\(6\) \(20\) \(\le 300\)

對於所有的數據,保證\(n\le 300\)\(m\le \frac{n(n-1)}{2}\)\(f_i、g_i \le n\)get

題解

題目大意:\(n\)個數以此入棧,問在知足\(m\)個形如\(f_i\)不能在\(g_i\)後出棧的限制的出棧序列數io

45%

咱們知道卡特蘭數有個推導公式是\(f_i=\sum_{i=1}^nf_i\times f_{n-i-1}\),這個公式其實是枚舉了最後出棧的數table

那麼擴展到這題,咱們將\(dp\)轉換爲區間\(dp\),枚舉\(k\)爲最後出棧的數,那麼有兩種狀況不合法:\(f=k\)或者\(f>k>g\)。當\(f=k\)的時候,\(f\)是最後出棧的,顯然不合法。而咱們知道,小於\(k\)老是比大於\(k\)的先出棧,因此當\(f>k>g\)時也是不合法的class

\(f[i][j]\)表示\(i\)\(j\)這個區間的合法出棧序列,那麼在上述兩種不合法的狀況不成立的狀況下,\(f[i][j]+=f[i][k-1]\times f[k+1][j]\)

時間複雜度\(O(n^3m)\),預計得分\(45\)

100%

考慮優化\(dp\),在\(O(1)\)的時間內判斷合不合法。不合法條件\(f>k>g\)成立,說明\(f>g\),那麼在讀入時\(f>g\)的放入平面直角座標系中,座標\((f,g)\),那麼能夠前綴和優化

記錄前綴和\(sm[i][j]\)\(l[i][j]\),分別記錄\(f>g\)以及全部的點,用來判斷\(f>k>g\)\(f=k\)的狀況

構造一個矩形

在這裏插入圖片描述

其中\(i,j,k\)分別是區間起點,終點,以及最後出棧的數

\(f=k\)說明\(l[k][j]-l[k][i-1]>0\),而若是矩形\(sm(i,i,j,k-1)-sm(i,i,k,j)>0\),說明有\(f>k>g\)的狀況,這兩種狀況都是不合法的

這樣的話時間複雜度優化到了\(O(n^3)\),預計得分\(100\)

Code

#include<cstdio>
#define mod 998244353
#define N 310
#define ll long long
using namespace std;
ll n,m,f[N][N],sm[N][N],al[N][N];
ll get(ll x,ll y,ll p,ll q) {return sm[x][y]-sm[x][q-1]-sm[p-1][y]+sm[p-1][q-1];}
int main()
{
	freopen("catalan.in","r",stdin);
	freopen("catalan.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for (ll i=1,x,y;i<=m;++i)
	{
		scanf("%lld%lld",&x,&y);
		if (x!=y)
		{
			if (x>y) ++sm[x][y];
			++al[x][y];
		}
	}
	for (ll i=1;i<=n;++i)
		for (ll j=1;j<=n;++j)
		{
			sm[i][j]=sm[i][j]+sm[i-1][j]+sm[i][j-1]-sm[i-1][j-1];
			al[i][j]=al[i][j]+al[i][j-1];
		}
	for (ll i=1;i<=n;++i)
		f[i][i]=f[i+1][i]=f[i][i-1]=1;
	for (ll len=2;len<=n;++len)
		for (ll i=1;i+len-1<=n;++i)
		{
			ll j=i+len-1;
			for (ll k=i;k<=j;++k)
			{
				ll x;
				if (k>i) x=get(j,k-1,i,i)-get(k,j,i,i);
				else x=0;
				ll y=al[k][j]-al[k][i-1];
				if (x<=0&&y<=0) f[i][j]=(f[i][j]+f[i][k-1]*f[k+1][j]%mod)%mod;
			}
		}
	printf("%lld\n",f[1][n]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
相關文章
相關標籤/搜索