CF1149C Tree Generator™

一、题目

点此看题

二、解法

话说老师给的课件是错的啊,把我坑了好久,我手玩样例才玩出来,最后只能去看洛谷题解了。

本题是树是用一个括号序列给出的,你要知道的是:( 代表递归下去到一个新节点,) 表示回溯到当前节点。首先这个括号序列的每一个区间都代表树上的一条路径,那么我们把能配对的括号消掉后剩下的是这种形式:)))((( ,因为能匹配的括号相当于回溯回来了,而剩下的就表示先回溯再往下走的路径。所以剩下的长度就是路径的长度,我们想要求出最长的就是直径

消去的过程好像不好算,我们可以把他转化为把原序列划分成两部分,( 看作 \(1\)) 看作 \(-1\),让后面的权值 \(-\) 前面的权值最大化即是路径长度。这种做法为什么能成立呢?因为 () 是不应该有贡献的,这种做法可以确保不会从 () 的中间划开,那么 () 的贡献就成为了 \(0\),那序列自然就变成了 )))((( 的形式,我们一定会从中间划开。但是作者确实不知道是怎么想到的,给括号赋值的思路值得学习。

但是这个东西可以维护?一般来说这种看似难维护但是可合并的东西我们考虑线段树,我们要维护下面几个值:

  • 区间权值和 \(sum\)
  • 区间最大的前缀和 \(lma\)
  • 区间最小的后缀和 \(rmi\)
  • 区间前缀的最佳划分答案 \(ld\)
  • 区间后缀的最佳划分答案 \(rd\)
  • 整个区间的划分答案 \(mad\)
  • 区间的某个区间划分答案(这个是真正的答案)\(mx\)

\(1-3\) 是很好维护的,我把 \(4\) 的维护方式讲一讲,剩下的就可以自己推了。分为 \(3\) 种情况:直接用左儿子的最优前缀;划分点在右儿子,用 \(ld[rs]-sum[ls]\) ;划分点在左儿子,用 \(mad[ls]+lma[rs]\)

真是毒瘤题啊,感觉思维和代码都很难的,如果 \(1-7\) 的转移不会的看看代码,结合定义理解吧!

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a[M];char s[M];
int sum[4*M],lma[4*M],rmi[4*M],ld[4*M],rd[4*M],mad[4*M],mx[4*M];
void up(int x)
{
	int i1=x<<1,i2=x<<1|1;
	sum[x]=sum[i1]+sum[i2];
	lma[x]=max(lma[i1],sum[i1]+lma[i2]);
	rmi[x]=min(rmi[i2],sum[i2]+rmi[i1]);
	ld[x]=max(ld[i1],max(ld[i2]-sum[i1],mad[i1]+lma[i2]));
	rd[x]=max(rd[i2],max(rd[i1]+sum[i2],mad[i2]-rmi[i1]));
	mad[x]=max(mad[i1]+sum[i2],mad[i2]-sum[i1]);
	mx[x]=max(max(mx[i1],mx[i2]),max(ld[i2]-rmi[i1],rd[i1]+lma[i2]));
}
void ins(int i,int l,int r,int id,int x)
{
	if(l==r)
	{
		sum[i]=x;
		lma[i]=max(x,0);
		rmi[i]=min(x,0);
		ld[i]=rd[i]=mad[i]=mx[i]=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=id) ins(i<<1,l,mid,id,x);
	else ins(i<<1|1,mid+1,r,id,x);
	up(i);
}
signed main()
{
	n=2*read()-2;m=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='(') a[i]=1;
		else a[i]=-1;
		ins(1,1,n,i,a[i]);
	}
	printf("%d\n",mx[1]);
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		if(a[x]==a[y]) continue;
		swap(a[x],a[y]);
		ins(1,1,n,x,a[x]);
		ins(1,1,n,y,a[y]);
		printf("%d\n",mx[1]);
	}
}
posted @ 2021-01-02 16:46  C202044zxy  阅读(91)  评论(0编辑  收藏  举报