cf629 C. Famil Door and Brackets
阿新 • • 發佈:2022-03-31
題意:
給定長為 m 的括號序列,在序列的左右兩邊各加一些括號,擴充套件成長為 n 的合法括號序列,求方案數。
\(1\le m\le n\le 1e5,n-m\le2000\),注意 n 不一定是偶數
思路:
似乎可以嗯算,但是看到 n-m 只有 2000,所以當然是列舉+dp更方便啦!
眾所周知,一個括號序列合法有兩個條件:左括號總數等於右括號總數,且任意字首中的左括號數大於等於右括號數。
列舉在左邊共放 \(i\) 個 \((\) 和 \(j\) 個 \()\),則 \(i\ge j\)。對於給定串 \(s\) 的中每個位置 \(p\),應有 \(i+precnt_( [p] \ge j+precnt_) [p]\implies i-j\ge \max\limits_p \{ precnt_)-precnt_( \}\)
知道了左邊放幾個,可以推出右邊放幾個。從右往左看,應有 \(j'-i'\ge \max\limits_p \{ sufcnt_(-sufcnt_) \}\)
預處理上述 \(\max\)
dp預處理放 \(i\) 個 \((\)、\(j\) 個 \()\) 的方案數 \(f(i,j)\) 。\(f(i,j)=f(i-1,j)+f(i,j-1)\)
媽的說了這麼多,其實就是個簡單列舉
const signed N = 1e5 + 3, M = 2003, mod = 1e9 + 7; ll n, m, f[M][M]; char str[N]; signed main() { f[0][0] = 1; //dp預處理 for(int i = 0; i < M; i++) for(int j = 0; j <= i; j++) { if(i) f[i][j] += f[i-1][j]; if(j) f[i][j] += f[i][j-1]; f[i][j] %= mod; } iofast; cin >> n >> m >> str + 1; if(n % 2) return cout << 0, 0; //特判奇數 int lef = 0, rig = 0; //s中的左/右括號總數 for(int i = 1; i <= m; i++) if(str[i] == '(') lef++; else rig++; int llim = 0, rlim = 0; //算一下限制 for(int i = 1, s = 0; i <= m; i++) { if(str[i] == ')') s++; else s--; llim = max(llim, s); } for(int i = m, s = 0; i; i--) { if(str[i] == '(') s++; else s--; rlim = max(rlim, s); } ll ans = 0; for(int i = 0; i + lef <= n/2; i++) for(int j = 0; j + rig <= n/2 && i - j >= llim; j++) { int ii = n/2 - i - lef, jj = n/2 - j - rig; if(jj - ii >= rlim) (ans += f[i][j] * f[jj][ii]) %= mod; } cout << ans; }