0%

CF-1073-D2. Sub-RBS (Hard Version)(注意虚拟节点可能的操作特判)(括号序列 dp 的状态设计)

题目大意

1. 核心定义

  • 括号序列比较 (“Better Than”):
    对于两个括号序列 aabb,满足以下条件之一则称 aabb “更好”:

    1. bbaa 的前缀,且 aba \neq b
    2. 在第一个不相同的字符位置 iiai=(a_i = '('bi=)b_i = ')'
  • 序列 tt 的得分 (Score)

    1. 如果 tt 不是正则括号序列 (RBS),得分为 00
    2. 如果 tt 是 RBS,查找其所有的正则括号子序列 rr。如果存在某些 rr 满足 rrtt “更好”,则得分为这些 rr 中长度 r|r| 的最大值。
    3. 若不存在这样的 rr,得分为 00

2. 任务要求

给定一个长度为 nn (1n1001 \le n \le 100) 的括号序列 ss,求 ss 的所有非空子序列的得分之和,结果对 998244353998244353 取模。


3. 样例解释

  • 样例 2:s = ()()()
    考虑其中一个子序列 t="()()()"t = "()()()"

    • 它是一个 RBS。
    • 它有一个子序列 r="(())"r = "(())"(取原序列第 1, 3, 4, 6 位)。
    • 比较 ttrr:第一位都是 (,第二位 t2=)t_2 = ')'r2=(r_2 = '('。根据定义,rrtt 更好。
    • rr 的长度为 44,经校验这是最长的更好子序列,故该子序列 tt 得分为 44
    • 其他子序列得分均为 00,总和为 44
  • 样例 3:****s = (())()

    • 对于子序列 t="(())()"t = "(())()",虽然它是 RBS,但无法找到比它“更好”的 RBS 子序列。
    • 例如,要在某位置 ii 更好,必须 ti=)t_i = ')'ri=(r_i = '(',但在该位置之前,tt 已经用完了所有的 ( 匹配了前面的 ),无法构造出更优的正则子序列。
    • 因此总和为 00

思路讲解

首先,D1 当中,我们发现一个规律,就是只要 ss 串只要是有答案的,其答案就是 s2|s|-2,那么其有答案的条件是第一个 )) 出现的位置后,又至少出现了出现了两个 ((

我们可以定义一个如下状态:

image

⚠️注意:这里的长度,个数,指的是合法方案个数及其合法方案的总(后缀)长度,这样子定义是为了方便记忆化搜索。

总体上的记忆化搜索过程可以描述为这样:

res(0,0)if unbalance=0state=3:res(1,1)lresdfs(idx,unbalance,state)if idx0:lreslenlreslen+lresnumresres+lres\begin{aligned} & res \gets (0,0) \\ & \text{if } unbalance = 0 \land state = 3: res \gets (1,1) \\ & lres \gets \text{dfs}(idx, unbalance, state) \\ & \text{if } idx \neq 0: lres_{\text{len}} \gets lres_{\text{len}} + lres_{\text{num}} \\ & res \gets res + lres\end{aligned}

那么之所以不在 idx=0idx=0 的时候进行这个操作,是因为 0 是虚拟节点,没有实际字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
auto dfs=[&](auto && self,int idx,int unbalance,int state) -> Len_num{
if (unbalance<0) {
return {0,0};
}
if (vis[idx][unbalance][state]) {
return dp[idx][unbalance][state];
}
vis[idx][unbalance][state]=true;
// assert(idx<=N);
Len_num res={0,0};
if (unbalance==0 && state==3) {
// 相当于只选这个 s【idx】的时候
res={1,1};
}
for (int i=idx+1;i<=N;++i) {
Len_num lres;
if (s[i]==')') {
lres=self(self,i,unbalance-1,max(1,state));
}else {
int to_state;
if (state==0) to_state=0;
else to_state=min(3,state+1);
lres=self(self,i,unbalance+1,to_state);
}
res+=lres;
// 0 是一个虚拟节点,不会增加这个 res 长度
if (lres.len!=0 && idx!=0) {
// 这里给所有的方案增加一个长度,即选了
res.len+=lres.num;
}
res%=mod;
}
dp[idx][unbalance][state]=res;
return res;
};
Len_num res=dfs(dfs,0,0,0);

AC代码

AC
https://codeforces.com/contest/2191/submission/358546432

心路历程(WA,TLE,MLE……)

res{0,0}unbalance=0 and state=3:res{1,1}lresdfs(idx,unbalance,state)idx0:lres.lenlres.len+lres.numresres+lresres\gets \{0,0\}\\ unbalance=0 \text{ and } state=3:res\gets \{1,1\} \\ lres\gets \text{dfs}(idx,unbalance,state)\\ idx≠0:lres.len\gets lres.len+lres.num\\ res\gets res+lres