CF556(div.2) E. Tree Generator

博客围绕有根树展开,以长度为2*(n - 1)的括号序列表示n个节点的有根树,给出q个交换括号操作,每次操作后输出树的直径。题解中通过公式d(u,v)=h(u)+h(v) - 2h(lca(u,v))求直径,用线段树维护相关值,合并时需注意。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

以长度为2∗(n−1)2*(n-1)2(n1)的括号的形式给出一棵n(3≤n≤100000)n(3\le n\le 100000)n(3n100000)个节点的有根树,初始位置在根节点处,从左向右遍历括号序列,其中左括号表示派生出一个新的儿子向下走,右括号表示回溯到父亲节点,即向上走。给出q(1≤q≤100000)q(1\le q \le 100000)q(1q100000)个操作,每个操作交换序列中两个括号,保证交换后仍然可以表示一颗有根树。对于每一次操作,输出这棵有根树的直径。

题解

假设h(u)h(u)h(u)表示uuu的深度,d(u,v)d(u,v)d(u,v)表示uuuvvv的距离,那么d(u,v)=h(u)+h(v)−2h(lca(u,v))d(u,v)=h(u)+h(v)-2h(lca(u,v))d(u,v)=h(u)+h(v)2h(lca(u,v)),直径就是d(u,v)d(u,v)d(u,v)的最大值,所以我们要维护等式右端的所有东西,即h(a)h(a)h(a)的最大值、h(a)−2h(b)h(a)-2h(b)h(a)2h(b)的最大值、−2h(b)-2h(b)2h(b)的最大值、−2h(b)+h(c)-2h(b)+h(c)2h(b)+h(c)的最大值、h(a)−2h(b)+h(c)h(a)-2h(b)+h(c)h(a)2h(b)+h(c)的最大值。维护的方法就是线段树,线段树上每个节点代表一段括号子序列,除了维护上面五个值,还需要维护一个这段括号序列的深度,注意,这里的深度是可以为负数的,比如说某个叶子节点只有一个右括号,那么深度就为−1-11。代码写起来不麻烦,关键就是合并时要多注意一下。

#include<bits/stdc++.h>
#define lson u<<1,l,mid
#define rson u<<1|1,mid+1,r
using namespace std;
int n,q;
char s[200005];
struct SEGMENTTREE{
	int d,mx1,mx2,mx3,mx4,mx5;
}t[200005<<2];
void maintain(int u){
	t[u].d=t[u<<1].d+t[u<<1|1].d;
	t[u].mx1=max(t[u<<1].mx1,t[u<<1].d+t[u<<1|1].mx1);
	t[u].mx2=max(t[u<<1].mx2,-2*t[u<<1].d+t[u<<1|1].mx2);
	t[u].mx3=max(max(t[u<<1].mx3,-t[u<<1].d+t[u<<1|1].mx3),t[u<<1].mx1-2*t[u<<1].d+t[u<<1|1].mx2);
	t[u].mx4=max(max(t[u<<1].mx4,-t[u<<1].d+t[u<<1|1].mx4),t[u<<1].mx2+t[u<<1].d+t[u<<1|1].mx1);
	t[u].mx5=max(max(t[u<<1].mx5,t[u<<1|1].mx5),
		max(t[u<<1].mx1-t[u<<1].d+t[u<<1|1].mx4,t[u<<1].mx3+t[u<<1].d+t[u<<1|1].mx1));
}
void se(int u,int x){
	if(s[x]=='('){
		t[u]=(SEGMENTTREE){1,1,0,0,1,1};
	}else{
		t[u]=(SEGMENTTREE){-1,0,2,2,1,1};
	}
}
void build(int u,int l,int r){
	if(l==r){
		se(u,l);
		return;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	maintain(u);
}
void update(int u,int l,int r,int x){
	if(l==r){
		if(s[x]=='(')s[x]=')';
		else s[x]='(';
		se(u,x);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)update(lson,x);
	else update(rson,x);
	maintain(u);
}
int main(){
	scanf("%d%d%s",&n,&q,s+1);
	build(1,1,2*(n-1));
	printf("%d\n",t[1].mx5);
	for(int x,y,i=1;i<=q;i++){
		scanf("%d%d",&x,&y);
		update(1,1,2*(n-1),x);
		update(1,1,2*(n-1),y);
		printf("%d\n",t[1].mx5);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值