題解:CF1264D Beautiful Bracket Sequence

喵仔牛奶發表於2024-09-03

太笨了,其他題解寫都沒寫的性質都看不出來……

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\)),答案即為:

\[\sum_{i=0}^{n}\sum_{j=0}^{n}j\binom{x_i}{j-a_i}\binom{y_i}{j-b_i} \]

複雜度 \(\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\),然後想辦法塞進組合數里。

\[\begin{aligned} &\sum_{j=0}^{n}j\binom{x_i}{j-a_i}\binom{y_i}{j-b_i}\\ =&\sum_{j=0}^{n}(j-a_i)\binom{x_i}{j-a_i}\binom{y_i}{j-b_i}+\sum_{j=0}^{n}a_i\binom{x_i}{j-a_i}\binom{y_i}{j-b_i}\\ \end{aligned} \]

\(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}\)

\[\begin{aligned} =&\sum_{j=0}^{n}x_i\binom{x_i-1}{j-a_i-1}\binom{y_i}{j-b_i}+\sum_{j=0}^{n}a_i\binom{x_i}{j-a_i}\binom{y_i}{j-b_i}\\ =&x_i\sum_{j=0}^{n}\binom{x_i-1}{j-a_i-1}\binom{y_i}{j-b_i}+a_i\sum_{j=0}^{n}\binom{x_i}{j-a_i}\binom{y_i}{j-b_i} \end{aligned} \]

範德蒙特卷積:\(\sum_{i=0}^{n}\binom{n}{i}\binom{m}{k-i}=\binom{n+m}{k}\)。我們接下來利用它來化簡。

\[\begin{aligned} &a_i\sum_{j=0}^{n}\binom{x_i}{j-a_i}\binom{y_i}{j-b_i}\\ =&a_i\sum_{j=0}^{n}\binom{x_i}{x-j+a_i}\binom{y_i}{j-b_i}\\ =&a_i\sum_{j=0}^{n}\binom{x_i}{x_i-j-b_i+a_i}\binom{y_i}{j}\\ =&a_i\binom{x_i+y_i}{x_i+a_i-b_i} \end{aligned} \]

解釋一下第三步,我們從列舉 \(j\) 變為列舉 \((j-b_i)\),因為原來 \(j\) 超出上下界時後面的項乘積為 \(0\),所以範圍不用特意改變。

同樣的方法化簡前面的:

\[\begin{aligned} &x_i\sum_{j=0}^{n}\binom{x_i-1}{j-a_i-1}\binom{y_i}{j-b_i}\\ =&x_i\sum_{j=0}^{n}\binom{x_i-1}{x_i-j+a_i}\binom{y_i}{j-b_i}\\ =&x_i\sum_{j=0}^{n}\binom{x_i-1}{x_i-j-b_i+a_i}\binom{y_i}{j}\\ =&x_i\binom{x_i+y_i-1}{x_i+a_i-b_i} \end{aligned} \]

答案化簡為

\[\sum_{i=0}^{n}a_i\binom{x_i+y_i}{x_i+a_i-b_i}+x_i\binom{x_i+y_i-1}{x_i+a_i-b_i} \]

時間複雜度 \(\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;
}

相關文章