题解:CF1264D Beautiful Bracket Sequence

太笨了,其他题解写都没写的性质都看不出来……

Solution

easy version#

我们考虑没有 ? 的串怎么做。由于能无限删,留下 (((...))) 这种肯定不劣。记 ai[1,i]( 数,bi(i,n]) 数,答案即为 maxi[1,n)min(ai,bi)

直接不太好做,最多也就是 O(n3) 的 DP。我们需要找一些性质。

性质 1:随着 i 变大,ai 不降,bi 不升,aibi 上升。

较为显然。

性质 2:对于任意括号串,有且仅有一个位置满足 min(ai,bi) 取到 maxai=bi

证明:

  • 随意找一个 min(ai,bi) 取到 max 的位置 p。令 xpapbp
  • xp>0,令 pp1,根据性质 1,xp 减小;
  • xp<0,令 pp+1,根据性质 1,xp 增大。
  • 重复执行以上步骤直至 xp=0。此时即 ap=bp
  • 因为 xixi1=1,x00,xn0,总能找到 xp=0 的位置。
  • 随着移动变化的仅为 ai,bi 中较大者(若不是,就有更优的答案),min(ai,bi) 不变。

我们称上面提到的位置为「中间位」。

ai[1,i] 未填时的 ( 数,bi(i,n] 未填时的 ) 数,xi[1,i]? 数,yi(i,n]? 数。我们枚举「中间位」i 与答案 j 来计算,需要保证左边恰好有 j(,右边恰好有 j),答案即为:

i=0nj=0nj(xijai)(yijbi)

复杂度 O(n2),可以通过 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#

对于硬版本,我们沿用前面的思路,考虑化简式子。

前面的 i=0n 很难化简,我们化简后面的那个求和。j 这个系数很讨厌,可以将其化为 (jai)+ai,然后想办法塞进组合数里。

j=0nj(xijai)(yijbi)=j=0n(jai)(xijai)(yijbi)+j=0nai(xijai)(yijbi)

m(nm) 的化简方法:m(nm)=n!mm!(nm)!=(n1)!n(m1)!(nm)!=n(n1m1)

=j=0nxi(xi1jai1)(yijbi)+j=0nai(xijai)(yijbi)=xij=0n(xi1jai1)(yijbi)+aij=0n(xijai)(yijbi)

范德蒙特卷积:i=0n(ni)(mki)=(n+mk)。我们接下来利用它来化简。

aij=0n(xijai)(yijbi)=aij=0n(xixj+ai)(yijbi)=aij=0n(xixijbi+ai)(yij)=ai(xi+yixi+aibi)

解释一下第三步,我们从枚举 j 变为枚举 (jbi),因为原来 j 超出上下界时后面的项乘积为 0,所以范围不用特意改变。

同样的方法化简前面的:

xij=0n(xi1jai1)(yijbi)=xij=0n(xi1xij+ai)(yijbi)=xij=0n(xi1xijbi+ai)(yij)=xi(xi+yi1xi+aibi)

答案化简为

i=0nai(xi+yixi+aibi)+xi(xi+yi1xi+aibi)

时间复杂度 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;
}
posted @   喵仔牛奶  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· 大模型工具KTransformer的安装
· [计算机/硬件/GPU] 显卡
点击右上角即可分享
微信分享提示
主题色彩