太笨了,其他題解寫都沒寫的性質都看不出來……
Solution
easy version
我們考慮沒有 ?
的串怎麼做。由於能無限刪,留下 (((...)))
這種肯定不劣。記 \(a_i\) 為 \([1,i]\) 的 (
數,\(b_i\) 為 \((i,n]\) 的 )
數,答案即為 \(\max_{i\in[1,n)}\min(a_i,b_i)\)。
直接不太好做,最多也就是 \(\mathcal{O}(n^3)\) 的 DP。我們需要找一些性質。
性質 1:隨著 \(i\) 變大,\(a_i\) 不降,\(b_i\) 不升,\(a_i-b_i\) 上升。
較為顯然。
性質 2:對於任意括號串,有且僅有一個位置滿足 \(\min(a_i,b_i)\) 取到 \(\max\) 且 \(a_i=b_i\)。
證明:
- 隨意找一個 \(\min(a_i,b_i)\) 取到 \(\max\) 的位置 \(p\)。令 \(x_p\) 為 \(a_p-b_p\)。
- 若 \(x_p>0\),令 \(p\gets p-1\),根據性質 1,\(x_p\) 減小;
- 若 \(x_p<0\),令 \(p\gets p+1\),根據性質 1,\(x_p\) 增大。
- 重複執行以上步驟直至 \(x_p=0\)。此時即 \(a_p=b_p\)。
- 因為 \(x_i-x_{i-1}=1,x_0\le 0,x_n\ge 0\),總能找到 \(x_p=0\) 的位置。
- 隨著移動變化的僅為 \(a_i,b_i\) 中較大者(若不是,就有更優的答案),\(\min(a_i,b_i)\) 不變。
我們稱上面提到的位置為「中間位」。
記 \(a_i\) 為 \([1,i]\) 未填時的 (
數,\(b_i\) 為 \((i,n]\) 未填時的 )
數,\(x_i\) 為 \([1,i]\) 的 ?
數,\(y_i\) 為 \((i,n]\) 的 ?
數。我們列舉「中間位」\(i\) 與答案 \(j\) 來計算,需要保證左邊恰好有 \(j\) 個 (
,右邊恰好有 \(j\) 個 )
,答案即為:
複雜度 \(\mathcal{O}(n^2)\),可以透過 easy version。
typedef Mint<mod> MI;
int n, x, y, a, b; MI rs; Comb<MI> C; string s;
int main() {
cin >> s, n = s.size(), s = "#" + s, C.init(n);
REP(i, 1, n) {
if (s[i] == '?') y ++;
if (s[i] == ')') b ++;
}
REP(i, 1, n) { // i=0 時貢獻必為 0,可以省略
if (s[i] == '(') a ++;
if (s[i] == '?') x ++, y --;
if (s[i] == ')') b --;
REP(j, 0, n)
rs += C(x, j - a) * C(y, j - b) * j;
}
cout << rs << '\n';
return 0;
}
hard version
對於硬版本,我們沿用前面的思路,考慮化簡式子。
前面的 \(\sum_{i=0}^{n}\) 很難化簡,我們化簡後面的那個求和。\(j\) 這個係數很討厭,可以將其化為 \((j-a_i)+a_i\),然後想辦法塞進組合數里。
\(m\binom{n}{m}\) 的化簡方法:\(m\binom{n}{m}=\frac{n!m}{m!(n-m)!}=\frac{(n-1)!n}{(m-1)!(n-m)!}=n\binom{n-1}{m-1}\)。
範德蒙特卷積:\(\sum_{i=0}^{n}\binom{n}{i}\binom{m}{k-i}=\binom{n+m}{k}\)。我們接下來利用它來化簡。
解釋一下第三步,我們從列舉 \(j\) 變為列舉 \((j-b_i)\),因為原來 \(j\) 超出上下界時後面的項乘積為 \(0\),所以範圍不用特意改變。
同樣的方法化簡前面的:
答案化簡為
時間複雜度 \(\mathcal{O}(n)\),可以透過 hard ersion。
typedef Mint<mod> MI;
int n, x, y, a, b; MI rs; Comb<MI> C; string s;
int main() {
cin >> s, n = s.size(), s = "#" + s, C.init(n);
REP(i, 1, n) {
if (s[i] == '?') y ++;
if (s[i] == ')') b ++;
}
REP(i, 1, n) { // i=0 時貢獻必為 0,可以省略
if (s[i] == '(') a ++;
if (s[i] == '?') x ++, y --;
if (s[i] == ')') b --;
rs += C(x + y, x + a - b) * a + C(x + y - 1, x + a - b) * x;
}
cout << rs << '\n';
return 0;
}