今天,接觸信息學不久的小\(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
咱們知道卡特蘭數有個推導公式是\(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\)
考慮優化\(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\)
#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; }