洛谷 P5658 [CSP-S2019] 括号树
链接:
分析:
显然我们应该在dfs树的同时维护每个点的答案。
注意到第 \(u\) 个点的答案可以分成两部分,不包含 \(u\) 点时的答案,和加入 \(u\) 点后新增的答案,前者可以从父节点继承下来,所以我们对于每个点考虑的是加入该点后新增的答案。
在dfs树时会回溯,所以我们还需要考虑撤销这个点带来的影响。
所以我们实际要做的就是思考出一种策略,使其能够对新加入的点维护出新增的答案,还能将这个点的影响撤销。
算法:
对于一个点,我们分类讨论左括号和右括号的两种操作。
对于加入点的策略。
首先让当前点继承父节点的答案,然后考虑新增答案。
如果加入的是右括号,它可能会与前面的第一个左括号匹配,所以考虑对左括号维护一个栈。考虑这两个括号中间的部分,它一定是一个合法子串或空串(假如中间有不匹配的右括号,那么它一定会和当前左括号匹配,假如中间有不匹配的左括号,那么它会成为第一个左括号,变成当前右括号的匹配对象)。所以当前右括号匹配后一定会新增一个答案,考虑其他的答案,一定是多组匹配的括号相连,形如\((\cdots)(\cdots)(\cdots)\)。所以我们用while循环查询当前now节点匹配的左括号的前一个是否是右括号且有匹配(匹配括号内部一样也是合法子串。同时我们需要对右括号维护一个匹配左括号):如果是,那么答案++,并将now更新为这个右括号,循环查询;如果不是,那么退出。这样就可以正确地维护出当前加入点的新增答案了。
如果加入的是左括号,那么不会有新增的答案(不可能与另一个右括号匹配),只需维护栈。
对于退出点时策略。
如果退出的是右括号,那么其他点的影响只有“可能占据了一个匹配的左括号“,重新在栈里加入这个左括号即可。
如果退出的是左括号,那么在栈里弹出即可。
优化:
基于以上算法,我们可以写出这样的程序:
void dfs(int u){
dp[u]=dp[fa[u]];
if(c[u]=='(') sta[++top]=u;
else{
if(top){
dp[u]++;
match[u]=sta[top--];
int now=u;
while(match[fa[match[now]]]){
now=fa[match[now]];
dp[u]++;
}
}
}
ans^=(u*dp[u]);
for(int i=head[u];i;i=e[i].next)
dfs(e[i].v);
if(c[u]=='(')top--;
else if(match[u])sta[++top]=match[u];
}
但是只获得了8AC,2TLE的好成绩。
反思整个算法,最可能影响时间复杂度的就是那个while循环了。我们看循环的过程,它实际是在不停向上跳,返回右括号的数量,对于一个右括号向上跳的结果是不会被后面的节点影响的,所以我们可以对每个右括号维护一个up值,对于一个右括号,只用在上一个右括号的up值加1就是当前的up值。新增的答案就是当前的up值了。
这样就把一个while循环优化成了 \(O(1)\) 查询。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
return p*f;
}
const int N=5e5+5;
int n;
char c[N];
struct edge{
int v,next;
}e[N];
int head[N],en;
void insert(int u,int v){
e[++en].v=v;
e[en].next=head[u];
head[u]=en;
}
int fa[N];
int sta[N],top;//left(
int match[N],up[N];//right)
int ans;
int dp[N];
void dfs(int u){
dp[u]=dp[fa[u]];
if(c[u]=='(') sta[++top]=u;
else if(top){
match[u]=sta[top--];
up[u]=up[fa[match[now]]]+1;
dp[u]+=up[u];
}
ans^=(u*dp[u]);
for(int i=head[u];i;i=e[i].next)
dfs(e[i].v);
if(c[u]=='(')top--;
else if(match[u])sta[++top]=match[u];
}
signed main(){
n=in;
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=2;i<=n;i++){
fa[i]=in;
insert(fa[i],i);
}
dfs(1);
cout<<ans;
return 0;
}
题外话:
两年前的普及组蒟蒻看到这题就放弃了场外提高,两年后的我看到这题在30分钟内成功AC!
洛谷 P5658 [CSP-S2019] 括号树的更多相关文章
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- 洛谷p3384【模板】树链剖分题解
洛谷p3384 [模板]树链剖分错误记录 首先感谢\(lfd\)在课上调了出来\(Orz\) \(1\).以后少写全局变量 \(2\).线段树递归的时候最好把左右区间一起传 \(3\).写\(dfs\ ...
- 括号树 noip(csp??) 2019 洛谷 P5658
洛谷AC通道 本题,题目长,但是实际想起来十分简单. 首先,对于树上的每一个后括号,我们很容易知道,他的贡献值等于上一个后括号的贡献值 + 1.(当然,前提是要有人跟他匹配,毕竟题目中要求了,是不同的 ...
- 洛谷 P3384 【模板】树链剖分
树链剖分 将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为"重边". 将其他的边标记为"轻边". 若果一个非根节点的子 ...
- 洛谷P3459 [POI2007]MEG-Megalopolis(树链剖分,Splay)
洛谷题目传送门 正解是树状数组维护dfn序上的前缀和,这样的思路真是又玄学又令我惊叹( 我太弱啦,根本想不到)Orz各路Dalao 今天考了这道题,数据范围还比洛谷的小,只有\(10^5\)(害我复制 ...
- [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?
其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...
- 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)
推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...
- 洛谷P2617 Dynamic Rankings (主席树)
洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...
- 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)
Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...
- 洛谷 P3359 改造异或树
题目描述 给定一棵n 个点的树,每条边上都有一个权值.现在按顺序删掉所有的n-1条边,每删掉一条边询问当前有多少条路径满足路径上所有边权值异或和为0. 输入输出格式 输入格式: 第一行一个整数n. 接 ...
随机推荐
- 法术迸发(Spellburst)
描述 法术迸发 (EN:Spellburst ) 是一种在<通灵学园>中加入的关键字异能,在玩家打出一张法术牌后触发,只能触发一次. 若随从在法术结算过程中死亡,则不会触发效果 思路 首先 ...
- Tomcat配置支持war包部署
Tomcat配置支持war包部署 #cat /data/tomcat/conf/server.xml <?xml version='1.0' encoding='utf-8'?> < ...
- SQL注入与burPsuit工具介绍
sql注入原理 原理:用户输入不可控,用户输入导致了sql语义发生了改变 用户输入不可控:网站不能控制普通用户的输入 sql语义发生变化: 动态网页介绍: 网站数据请求 脚本语言:解释类语言,如,后端 ...
- AtCoder Regular Contest 069 D - Menagerie 枚举起点 模拟递推
arc069.contest.atcoder.jp/tasks/arc069_b 题意:一堆不明身份的动物排成一圈,身份可能是羊或狼,羊一定说实话,狼一定说假话.大家各自报自己的两边是同类还是不同类, ...
- gentoo(贱兔) Linux作业系统的基本使用
emerge是gentoo linux的portage包管理器的命令行工具emerge的基础使用:emerge 软件包名:安装某软件包 emerge nanoemerge --ask 软件包名:交互式 ...
- 【OI】Tex Quotes——UVa 272
题目: TEX is a typesetting language developed by Donald Knuth. It takes source text together with a fe ...
- Java基础系列(11)- 变量、常量、作用域以及变量的命名规范
变量 变量是什么:就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域 type varName [=valu ...
- 『GoLang』面向对象
我们总结一下前面看到的:Go 没有类,而是松耦合的类型.方法对接口的实现. 面向对象语言最重要的三个方面分别是:封装,继承和多态,在 Go 中它们是怎样表现的呢? Go实现面向对象的两个关键是stru ...
- HTML 网页开发、CSS 基础语法——七.HTML常用标签
标题标签(h1-h6) 1.标题标签 ① 标题(Heading),通过<h1>-<h6>六个标签分别来对六个级别的标题进行性定义的. ② <h1>是级别最高,也是字 ...
- 关于国密HTTPS 的那些事(二)
关于国密HTTPS 的那些事(二) 三. 需要解决的问题 前文我们了解了https,并梳理了国密https的流程.那么完成这些流程的目的是什么呢?又是怎么来保护数据的安全性呢?我们继续... 上文我们 ...