P7914 [CSP-S 2021] 括号序列 题解

I_am_Accepted

2021-10-23 22:55:54

Solution

Upd 2022-1-26:更新了 LaTeX 和标题层级。(更好的阅读体验 OvO) # Preface 感觉比 T3 难好多 qwq。 纪念我考场上写错的 $O(n^3)$ 区间 DP(边界条件没判 qwq)。 本文半数讲思路过程,所以本题解可能稍长,但最终得出的结论和公式是简洁滴~(大佬们可以直接跳转至 Algorithm 或 Code 部分) # Analysis 这括号序列题一般是区间 DP。 单纯的想法是设 $dp_{l,r}$ 为 $[l,r]$ 这段子串为合法超级括号序列的方案数。 但这样有问题: ![](https://cdn.luogu.com.cn/upload/image_hosting/dqn9y54t.png) ($A,B,C,D,E$ 均为合法超级括号序列,$S$ 为题面中定义) 如图,此种情况会算两遍。 # Improve 设 $f_{l,r}$ 表示 $[l,r]$ 这段子串为合法超级括号序列**且两端($l$ 和 $r$)的括号匹配**($\mathtt{integral}$)的方案数。 ![](https://cdn.luogu.com.cn/upload/image_hosting/adb4xxzv.png) 设 $g_{l,r}$ 表示 $[l,r]$ 这段子串为合法超级括号序列**且两端($l$ 和 $r$)的括号不匹配**($\mathtt{separated}$)的方案数。 ![](https://cdn.luogu.com.cn/upload/image_hosting/sul4mzmf.png) 这样就能有效避免重复算的情况。 # Algorithm 区间 DP 基本:**按照子串长度从小到大枚举**。 设现在遍历到 $f_{l,r}$ 和 $g_{l,r}$,$len=r-l+1$。 设 $(bool)X_{l,r}$ 表示 $[l,r]$ 是($1$)否($0$)全为 `'*'` 和 `'?'`($O(n^2)$ 预处理)。 (设原字符串为 $s$,从 $1$ 标号,与 DP 数组对齐) ## (一)特判 若 $s_l$ **不能成为** `'('` 或 $s_r$ **不能成为** `')'`,直接 `continue` 掉。 若 $len=2$,直接 $f_{l,r}=1$,`continue` 掉。 ## (二) $\mathtt{integral}$ ### (S) $$f_{l,r}+=[X_{l+1,r-1}=true]$$ ### (A) $$f_{l,r}+=f_{l+1,r-1}+g_{l+1,r-1}$$ ### (SA) $$f_{l,r}+=\sum\limits^{k}_{i=1}(f_{l+i+1,r-1}+g_{l+i+1,r-1})[X_{l+1,l+i}=true]$$ ### (AS) $$f_{l,r}+=\sum\limits^{k}_{i=1}(f_{l+1,r-i-1}+g_{l+1,r-i-1})[X_{r-i,r-1}=true]$$ ## (三) $\mathtt{separated}$ ### ASB & AB 这部分比较麻烦,容易算重,需要谨慎。 我们规定 $B$ 为 $\mathtt{integral}$ 子串,这样就不重不漏了。 $$g_{l,r}+=\sum\limits_{l<i<j<r,j-i-1\leqslant k}((f_{l,i}+g_{l,i})\times f_{j,r})[X_{i+1,j-1}=true]$$ (规定 $X_{a,a-1}=true$) # Optimization 发现 **ASB & AB** 部分为 $O(n^4)$,而你的愿望是 AK CSP-S 2021,怎么办呢? 思考:上面柿子中若 $i$ 增减 $1$ 则**可取的 $j$ 的范围变化量为 $O(1)$**。 优化成 $O(n^3)$。 为防止思维固化,请读者自行思考优化的具体方式(很简单的 DP 优化,若真不会则看代码)。 # Code 别忘了取模。 ```cpp #include<bits/stdc++.h> using namespace std; #define ll long long #define For(i,j,k) for(register int i=j;i<=k;i++) #define Rof(i,j,k) for(register int i=j;i>=k;i--) #define ckmx(a,b) if(a<b){a=b;} #define N 510 #define madd(a,b) a=(a+b%mod)%mod; const ll mod=1000000007; char s[N]; int n,k,R; ll f[N][N],g[N][N],tmp,cur[N]; int id,nxt[N]; bool X[N][N]; signed main(){ scanf("%d%d",&n,&k); scanf("%s",s+1); For(l,1,n) For(r,l,n) X[l][r]=0; For(l,1,n){ if(s[l]!='?' && s[l]!='*') continue; X[l][l]=1; For(r,l+1,n){ if(s[r]=='?' || s[r]=='*'){ X[l][r]=1; } else break; } } For(len,2,n){ int r; For(l,1,n-len+1){ r=l+len-1; f[l][r]=g[l][r]=0; if((s[l]!='(' && s[l]!='?') || (s[r]!=')' && s[r]!='?')) continue; if(l+1==r){ madd(f[l][r],1); continue; } //integral if(r-l-1<=k && X[l+1][r-1]){ madd(f[l][r],1); } madd(f[l][r],f[l+1][r-1]+g[l+1][r-1]); For(l1,l+1,min(r-2,l+k))//记得取 min,我考试挂在这 if(X[l+1][l1]) madd(f[l][r],f[l1+1][r-1]+g[l1+1][r-1]); Rof(r1,r-1,max(l+2,r-k))//记得取 max,我考试挂在这 if(X[r1][r-1]) madd(f[l][r],f[l+1][r1-1]+g[l+1][r1-1]); //separated id=0; For(i,l+1,r-2){ if(id<=i) id=i+1; while((s[id]=='*' || s[id]=='?') && id<r-1) id++; nxt[i]=min(i+k+1,id); } tmp=0; For(j,l+2,nxt[l+1]){ madd(tmp,f[j][r]); } madd(g[l][r],(f[l][l+1]+g[l][l+1])%mod*tmp); For(i,l+2,r-2){ madd(tmp,mod-f[i][r]); For(j,nxt[i-1]+1,nxt[i]) madd(tmp,f[j][r]); madd(g[l][r],(f[l][i]+g[l][i])%mod*tmp); } } } printf("%lld\n",(f[1][n]+g[1][n])%mod); return 0; } ```