【点分治】括号(2022.5.28)

题目

2.1 题目描述

有一棵n 个点的无向树,每个点上有一个标记,为'('或')',对于一个有序点对 (u,v),若
从 u 到 v (包含u,v)的有向路径上的所有标记组成了一个合法的括号序列,则称这是一
个完美点对,请求出有多少个完美点对。

2.2 输入格式

第一行为一个整数 n,表示点数;
第二行为一个由’(‘和’)’组成的字符串,表示每个点上的标记;
接下来n-1 行,每行两个整数u, v,表示u,v 之间有一条无向边。

2.3 输出格式

输出一行一个整数,表示完美点对的数目。

2.4 样例输入

4 
(()) 
1 2 
2 3 
3 

2.5 样例输出

2 

2.6 数据范围与约定

对于前20%的数据 n≤100;
对于100%的数据,1≤n≤105。

解思

第一眼看感觉像是点分治,觉得应该不会考来着于是就先看第三题去了(最后打暴力写炸了..)

没想到真的是点分治

算是点分治的模板题吧,每层找重心然后统计过重心的路径贡献

注意此题括号序列是否成立的细节

上代码!

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int f=1,j=0;char w=getchar();
	while(w>'9'||w<'0'){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(w>='0'&&w<='9'){
		j=(j<<3)+(j<<1)+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=100001,T=100000;
int head[N],to[N*2],front[N*2],tail;
int n,sum[N],cnt[N*3][2],size[N],mid[N*3][2];//cnt->0:'('starts   cnt->1:')'starts 
int root_sum,maxn,minn,maxn1,minn1;
long long ans;
bool walk[N];
char s[N];
void addline(int x,int y){
	to[++tail]=y;
	front[tail]=head[x];
	head[x]=tail;
	return ;
}
void getsize(int nown,int fa){
	size[nown]=1;
	for(int k=head[nown];k;k=front[k]){
		int x=to[k];
		if(x==fa||walk[x])continue;
		getsize(x,nown);
		size[nown]+=size[x];
	}
	return ;
}
int getcore(int nown,int fa,int all_size){
	for(int k=head[nown];k;k=front[k]){
		int x=to[k];
		if(walk[x]||x==fa)continue;
		if(size[x]>all_size/2)return getcore(x,nown,all_size);
	}
	return nown;
}
void getcnt(int nown,int fa,int nowsum,int ll,int rr){
	nowsum+=sum[nown];
	if(ll<0&&sum[nown]>0)ll++;
	if(sum[nown]<0)ll--;
	if(rr>0&&sum[nown]<0)rr--;
	if(sum[nown]>0)rr++; 
	if(nowsum<=0&&sum[nown]<0&&rr==0){
		ans+=cnt[T-nowsum][0];
		mid[T+nowsum-root_sum][1]++;
		minn1=min(minn1,T+nowsum-root_sum);
		maxn1=max(maxn1,T+nowsum-root_sum);
	}
	if(nowsum>=0&&sum[nown]>0&&ll==0){
		ans+=cnt[T-nowsum][1];
		mid[T+nowsum-root_sum][0]++;
		minn1=min(minn1,T+nowsum-root_sum);
		maxn1=max(maxn1,T+nowsum-root_sum);
	}
	for(int k=head[nown];k;k=front[k]){
		int x=to[k];
		if(x==fa||walk[x])continue;
		getcnt(x,nown,nowsum,ll,rr);
	}
	return ;
}
void work(int nown){
	getsize(nown,0);
	int core=getcore(nown,0,size[nown]);
	walk[core]=true;
	for(int k=head[core];k;k=front[k]){
		int x=to[k];
		if(walk[x])continue;
		work(x);
	}
	walk[core]=false;
	root_sum=sum[core];
	maxn=T;minn=T;
	cnt[T][0]=cnt[T][1]=1;
	for(int k=head[core];k;k=front[k]){
		int x=to[k];
		if(walk[x])continue;
		maxn1=T;minn1=T;
		getcnt(x,core,root_sum,0,0);
		for(int i=minn1;i<=maxn1;i++)cnt[i][0]+=mid[i][0],cnt[i][1]+=mid[i][1],mid[i][0]=mid[i][1]=0;
		minn=min(minn,minn1);
		maxn=max(maxn,maxn1);
	}
	for(int i=minn;i<=maxn;i++)cnt[i][0]=cnt[i][1]=0;
	return ;
}
signed main(){ 
	n=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++){
		if(s[i]=='(')sum[i]=1;
		else sum[i]=-1;
	}
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		addline(x,y);addline(y,x);
	}
	work(1);
	printf("%lld",ans);
	return 0;
}
posted @   flywatre  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示