洛谷 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. 接 ...
随机推荐
- SparkPi的编程计算
Pi的计算方式有很多,本文主要是通过Spark在概论统计的方法对Pi进行求解: 算法说明: 在边长为R的正方形中,其面积为R^2,而其内接圆的面积为pi * R^2 /4 ,圆的面积与正方形的面积比为 ...
- 基于django2.2的网页构建
安装django pip install django==2.2 建一个在线商城的项目 django-admin startproject pyshop 启动项目 python manage.py r ...
- discuz连接微博登陆,第三方登录
首先记一下discuz的ucenter的架构: ucenter 是用户中心.其他的应用都是和ucenter连接,包括discuz也是ucenter的一个应用(默认的); 第一步: 在ucenter新建 ...
- mysql 基础配置经验
创建库: 排序:utf8_unicode_ci和utf8_general_ci对中.英文来说没有实质的差别.utf8_general_ci校对速度快,但准确度稍差. 普遍的意思utf8_unicode ...
- 『GoLang』接口
接口是什么 Go 语言不是一种 "传统" 的面向对象编程语言:它里面没有类和继承的概念. 但是 Go 语言里有非常灵活的 接口 概念,通过它可以实现很多面向对象的特性.接口提供了一 ...
- 【月光宝盒get√】用时间置换空间,聊聊稀疏数组的那些事儿
背景 数据结构是指带有结构特性的数据元素的集合.在数据结构中,数据之间通过一定的组织结构关联在一起,便于计算机存储和使用.从大类划分,数据结构可以分为线性结构和非线性结构,适用于不同的应用场景. 线性 ...
- mysql的一次意外
打开navcat连接本地mysql数据库的时候说mysql服务无法连接,切换到cmd用命令行来启动报错,发生系统错误5,查看百度,需用管理员权限运行, 用管理员运行依旧不好使 C:\WINDOWS\s ...
- 计算机网络-4-1-2-分类的IP地址
有关IP最重要的文档就是互联网的正式标准RFC 791 IP地址及其表示方法 整个互联网就是一个单一,抽象的网络,IP地址就是给互联网上的每一台主机(或者路由器)的每一个接口分配一个在全世界范围内都是 ...
- 模拟一个简单的tomcat
目录 简单处理 每个请求一个线程 模拟tomcat 参考 简单处理 // 客户端和服务器的通信,说到底就是两个数据的传输, // 客户端发送inputStream给服务器,服务器回复 // outpu ...
- 为什么'\x1B'.length===1?\x与\u知识延伸
背景 先讲一下背景,再说原因 大多数库都会在日志中使用chalk库为console的内容进行上色 被chalk处理后,其原本的内容会被'\x1B...'所包裹 console.log(chalk.bl ...