P5658 括号树
Posted lpf-666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P5658 括号树相关的知识,希望对你有一定的参考价值。
P5658 括号树
经典的括号树,树形DP
(送我退役的括号树)
题意描述
相信我已经把这道题深深地记忆在我的脑海之中(想看出门)
第一年参加提高组,心情好自闭呀。
考场上只拿了链的35分的蒟蒻我,终于在靠后想出了正解。
算法分析
树形DP,但首先可以不用管它,先看部分分。
10~20pts
暴力O(N4)的。是个人都会写吧。
55pts
把链的分全拿了,观察我们暴力究竟慢在哪里了?
无非就是计算 1~i之中有多少个匹配的括号子序列。
观察数据,发现数据 5e5,所以只能O(N);
那么就要O(1)求解匹配的括号子序列,发现具有无后效性,于是果断DP.
过了很久很久…
手玩几组数据后我终于发现了规律:
if(这是右括号并可以匹配) a[i]=a[左括号的位置-1]+1;f[i]=f[i-1]+a[i];else 左括号入栈(当然入的是左括号的坐标)
100pts
终于到正解了。
果断
解决了链,有了稳稳的 55pts。想想好像可以实现化链成树,果断开正解。
很明显,我们解决链的做法在树里有很多行不通的地方。
细细想来,困难主要出现在这2个方面。
首先,你没法遍历整颗树的时候编号是连续的。这代表着我们 lst[i] = lst[t - 1] + 1 这样计算是完全行不通了。
其次,遍历一棵树必然会有递归和回溯。而处理链我们不考虑回溯,一直向下找就可以找完了。
冷静。
先看第一个问题
冷静分析一波,你会发现,虽然编号不连续了,但是你的括号序列一定是从父节点传递下来的!
仔细一想,我们发现,在链的情况里,为什么能用 lst[i] = lst[t - 1] + 1 计算贡献?其实,t?1 就是 t 的父亲节点!无非是 [t,i] 的括号序列继承了 [1,t?1]也就是 [1,fa[t]] 的括号序列!(fa[i]代表 i 的父亲)
然后我们惊喜的发现,这条定则对于树完全适用。
于是我们就可以修改一波原来的式子:
$lst[i] = lst[t - 1] + 1 ?> lst[x] = lst[fa[t]] + 1; $
perfect!
当然计算总答案也要修改:
(sum[i] = sum[i - 1] + lst[i]; ?> sum[x] = sum[fa[x]] + lst[x];)
这样我们就解决了问题1
接下来考虑解决第二个问题:
在树中遍历有回溯,回溯后栈里的信息可能就无法对应当前的版本...
其实这个问题很容易解决。由于每次回溯只回溯一层,所以我们在回溯的时候,执行我们递归时相反的操作即可!
比如,如果我们扫到右括号,递归时如果栈不为空,照理来说会弹出一个位置信息。
那么我们就可以记录这个信息,回溯的时候再把它压回去,又变成了我们当前的版本。
扫到左括号也一样。我们会压入一个位置信息,那么回溯时,直接弹出这个压入的信息就可以了!
其实这也相当于“复原”操作,让栈里的信息永远留在我们现在的状态!
没了
代码实现
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int n,fa[500050],zhan[500050],tot=0;
long long step[500050],sum[500050],ans;
char s[500050];
vector<int>side[500050];
void dfs(int x){
int last=0;
if(s[x]==')'){
if(tot){
last=zhan[tot];
step[x]=step[fa[last]]+1;
tot--;
}
}
else if(s[x]=='(') zhan[++tot]=x;
sum[x]=sum[fa[x]]+step[x];
for(int i=0;i<side[x].size();i++) dfs(side[x][i]);
//回溯
if(last) zhan[++tot]=last;
else if(tot) tot--;
return;
}
int main(){
scanf("%d",&n);
cin>>s+1;
for(int i=2;i<=n;i++){
scanf("%d",&fa[i]);
side[fa[i]].push_back(i);
}
dfs(1);
for(long long i=1;i<=n;i++){
ans^=sum[i]*i;
//printf("%lld
",sum[i]);
}
printf("%lld",ans);
//system("pause");
return 0;
}
结语
一步一步分析,这道题是不是就没有这么难了?
这也告诉了我们,考场上,一开始绝对不要先想正解,先看一看部分分,对你想正解有帮助哦!
很明显,我们这次解题的步骤就是由 暴力 -> 链 -> 正解的!
没了。。。
以上是关于P5658 括号树的主要内容,如果未能解决你的问题,请参考以下文章
asp.net 使用正则表达式验证包含打开/关闭括号片段的属性字符串