Luogu P5658 括号树
Luogu P5658 括号树
来补一道当年考场上没做出来的题。
不难想到树上 DP,关键在于如何设置函数与转移。
按题意,记 $k_i$ 为 以 $s_i$ 结尾的串中的合法子串数;记 $cnt_i$ 为 以 $s_i$ 结尾的合法子串数。请注意这两者的差别,这样做的动机会在转移方程处进行说明。
现在考虑转移,我们首先需要考虑当前节点的括号匹配情况,这可以用一个全局栈来维护。具体的实现方式是:
- 若当前节点为左括号,则将当前节点的编号推进栈中;
- 若当前节点为右括号,且栈为空,则说明此右括号无法匹配,不需要对栈操作;
- 若当前节点为右括号,且栈不空,则说明此右括号可以匹配,匹配到的左括号的节点编号即为栈顶记录的编号,将栈顶元素弹出。
那么基于当前节点(记为节点 $x$)的括号匹配情况,我们得到以下转移方程:
- 若当前节点为左括号,则 $cnt_x=0, k_x=k_{fa(x)}$;
- 若当前节点为右括号,且栈为空,则 $cnt_x=0, k_x=k_{fa(x)}$;
- 若当前节点为右括号,且栈不空,则记匹配到的左括号的节点编号为 $y$,有 $cnt_x=cnt_{fa(y)}+1, k_x=k_{fa(x)}+cnt_x$。
先解释 $cnt_x$ 的转移,注意到 $cnt_x$ 只在第三种情况下会发生转移:当 $s_x$ 与 $s_y$ 发生匹配后,考虑到 $cnt_x$ 的含义,则以 $s_i$ 结尾的合法子串有 $\overline{s_j \cdots s_i}$ 这一个 与 以 $s_{fa(y)}$ 结尾的合法子串接上 $\overline{s_j \cdots s_i}$ 共 $cnt_{fa(y)}$ 个。
这也就说明了为什么要将 $k_{fa(y)}$ 与 $cnt_{fa(y)}$ 分开记录。因为 $k_{fa(y)}$ 表示的是以 $s_{fa(y)}$ 结尾的串中的合法子串数,若是这些合法子串不以 $s_{fa(y)}$ 结尾,则不能与之后的 $\overline{s_j \cdots s_i}$ 连接起来作为一个新的合法子串。
而 $k_x$ 的转移则不难理解。若 $s_x$ 不发生匹配,则 $k_x$ 继承 $k_{fa(x)}$ 的值;否则就在继承 $k_{fa(x)}$ 的值的基础上再加上新增的以 $s_x$ 结尾的合法子串数,即 $cnt_x$。
代码实现上,利用 DFS 即可。要注意搜索的回溯。
#include<bits/stdc++.h>
#define N 500010
int n;
long long ans;
char str[N];
std::vector <int> v[N];
std::stack <int> s;
struct node {
int fa;
long long k,cnt;
char c;
};
node a[N];
namespace WalkerV {
void Read() {
scanf("%d%s",&n,str+1);
for(int i=1;i<=n;i++) {
a[i].c=str[i];
}
for(int i=2;i<=n;i++) {
scanf("%d",&a[i].fa);
}
return;
}
void DFS(int x) {
if(a[x].c=='(') {
a[x].k=a[a[x].fa].k;
s.push(x);
for(int i=0;i<v[x].size();i++) {
DFS(v[x][i]);
}
s.pop();
}
else if(a[x].c==')') {
if(s.empty()) {
a[x].k=a[a[x].fa].k;
for(int i=0;i<v[x].size();i++) {
DFS(v[x][i]);
}
}
else {
int y=s.top();
s.pop();
a[x].cnt=a[a[y].fa].cnt+1,a[x].k=a[x].cnt+a[a[x].fa].k;
for(int i=0;i<v[x].size();i++) {
DFS(v[x][i]);
}
s.push(y);
}
}
return;
}
void Solve() {
for(int i=1;i<=n;i++) {
v[a[i].fa].push_back(i);
}
DFS(1);
for(int i=1;i<=n;i++) {
ans^=(long long)i*a[i].k;
}
return;
}
void Print() {
printf("%lld\n",ans);
return;
}
}
int main()
{
WalkerV::Read();
WalkerV::Solve();
WalkerV::Print();
return 0;
}