【Codeforces】 CF1149C. Tree Generator™

题目链接

Codeforces方向

Luogu方向

题目解法

首先给出一个引理

一段序列去掉所有的匹配括号之后,剩下的序列一定组成一条链,且这条链的长度就是剩下序列的长度

可以粗糙地证明一下:

学过了树上莫队的知道:原题中的括号序列可以转化为欧拉序

树上的一条路径可以分成 2 种情况,记 first[x] 为 x 第一次在欧拉序中出现的位置, last[x] 同理,这里默认 first[u] < first[v]

  1. last[u] > last[v],那么 u 一定是 v 的祖先,那么 first[u] 到 first[v] 去掉匹配的就是 u 到 v 路径的长度 +1
    一条像这样的路径只有通到根节点 1 才是最优的,但是 1 并不在括号序列内,所以 +1 可以抵消掉
  2. last[u] < last[v],那么 u 一定不是 v 的祖先,从 last[u] 到 first[v] 去掉匹配的路径是不包括 lca 的,因为点数 = 边数 +1,所以成立

其实上面画几个图感性理解一下就可以了

观察去掉匹配括号后,序列一定是 ))…)(…(( 类似这样的,其中 ( 和 ) 都可以没有

我们考虑把 ( 赋值为 1,) 赋值为 -1

那么路径的长度一定是找到一个点 p,从 p 往后的前缀和 - 从 p 往前的后缀和

从中找到的最大值

这个东西可以用线段树维护一下

想象需要记录那些东西:

前缀的最大相减值,后缀的最大相减值,把 [ l,r ] 填满的最大相减值, 总和,前缀和最大值,后缀和最小值,答案

通过这些值可以进行转移

#include <bits/stdc++.h>
using namespace std;
const int N(200100);
int n,m;
char c[N];
inline int max3(int x,int y,int z){
	return max(max(x,y),z);
}
inline int max4(int x,int y,int z,int k){
	return max(max(x,y),max(z,k));
}
struct SegmentTree{//维护前缀和数组 
	struct Node{
		int lmax,rmax,fillmax,sum,premx,sufmn,ans;
	}seg[N<<2];
	void pushup(int x){
		seg[x].sum=seg[x<<1].sum+seg[x<<1^1].sum;
		seg[x].fillmax=max(seg[x<<1].fillmax+seg[x<<1^1].sum,seg[x<<1^1].fillmax-seg[x<<1].sum);
		seg[x].lmax=max3(seg[x<<1].lmax,seg[x<<1].fillmax+seg[x<<1^1].premx,seg[x<<1^1].lmax-seg[x<<1].sum);
		seg[x].rmax=max3(seg[x<<1^1].rmax,seg[x<<1^1].fillmax-seg[x<<1].sufmn,seg[x<<1^1].sum+seg[x<<1].rmax);
		seg[x].premx=max(seg[x<<1].premx,seg[x<<1].sum+seg[x<<1^1].premx);
		seg[x].sufmn=min(seg[x<<1^1].sufmn,seg[x<<1^1].sum+seg[x<<1].sufmn);
		seg[x].ans=max4(seg[x<<1].ans,seg[x<<1^1].ans,seg[x<<1].rmax+seg[x<<1^1].premx,seg[x<<1^1].lmax-seg[x<<1].sufmn); 
	}
	void build(int l,int r,int x){
		if(l==r){
			if(c[l]=='(')
				seg[x].lmax=seg[x].rmax=seg[x].fillmax=seg[x].sum=seg[x].premx=seg[x].ans=1,seg[x].sufmn=0;
			else
				seg[x].sum=seg[x].sufmn=-1,seg[x].fillmax=seg[x].lmax=seg[x].rmax=seg[x].ans=1,seg[x].premx=0;
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,x<<1),build(mid+1,r,x<<1^1);
		pushup(x);
//		cout<<l<<' '<<r<<" : "<<seg[x].sum<<' '<<seg[x].fillmax<<' '<<seg[x].lmax<<' '<<seg[x].rmax<<' '<<seg[x].premx<<' '<<seg[x].sufmn<<' '<<seg[x].ans<<'\n';
	}
	void modify(int l,int r,int x,int p,int k){
		if(l==r){
			if(k==1)
				seg[x].lmax=seg[x].rmax=seg[x].fillmax=seg[x].sum=seg[x].premx=seg[x].ans=1,seg[x].sufmn=0;
			else
				seg[x].sum=seg[x].sufmn=-1,seg[x].fillmax=seg[x].lmax=seg[x].rmax=seg[x].ans=1,seg[x].premx=0;
			return;
		}
		int mid=(l+r)>>1;
		if(mid>=p)
			modify(l,mid,x<<1,p,k);
		else
			modify(mid+1,r,x<<1^1,p,k);
		pushup(x);
//		cout<<l<<' '<<r<<" : "<<seg[x].sum<<' '<<seg[x].fillmax<<' '<<seg[x].lmax<<' '<<seg[x].rmax<<' '<<seg[x].premx<<' '<<seg[x].sufmn<<' '<<seg[x].ans<<'\n';
	}
}SGT;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			RR=-1;
	for(;isdigit(ch);ch=getchar())
		FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
int main(){
	n=read(),m=read();n=(n-1)<<1;
	scanf("%s",c+1);
	SGT.build(1,n,1);
	cout<<SGT.seg[1].ans<<'\n';
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		if(c[x]!=c[y]){
			if(c[x]=='(')
				SGT.modify(1,n,1,x,-1),SGT.modify(1,n,1,y,1);
			else
				SGT.modify(1,n,1,x,1),SGT.modify(1,n,1,y,-1);
			swap(c[x],c[y]);
		}
		printf("%d\n",SGT.seg[1].ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值