Luogu P9655 Beside You

题目传送门

思路

我们令 (\(1\))\(-1\),然后设 \(s\) 为前缀和数组。

我们考虑什么样的一条路径链是合法的。假设这条链两端点为 \(u,v\),那么必然有 \(s_{fa_u}=s_v\) 且路径上 \(s\) 的最小值等于 \(s_v\)

于是我们得出了一条性质,假如我们的根是选定的,那么能成为叶子节点的点也唯一确定了,且能成为叶子节点的点数之和为 \(n\)

注意到有一个点数之和的限制,这使得我们想到虚树。

于是我们枚举根的权值,然后找出所有该权值的点,建出虚树,然后 dp 一下即可。

转移方程是简单的,判断一个点的某个儿子到他的路径是否合法,如果合法直接加上儿子的 dp 值即可。

至于判断路径是否合法,可以在建边时预先判断,使用树剖和线段树即可。

时间复杂度 \(O(n\log n)\)

代码

#include<bits/stdc++.h>
#define int long long
#define N 500005
#define pii pair<int,int>
#define x first
#define y second
#define mod 1000000007
#define inf 2e18
using namespace std;
int T=1,n,cur;
char c[N];
vector<int>e[N],v[N<<1];
void add(int a,int b){
	e[a].push_back(b);
}
struct tc{
	int dep[N],fa[N],siz[N],son[N],top[N];
	int dfn[N],tot,s[N],nw[N];
	void dfs1(int u,int f){
		dep[u]=dep[f]+1;
		fa[u]=f;
		siz[u]=1;
		for(auto j:e[u]){
			if(j==fa[u])continue;
			if(c[j]=='(')s[j]=s[u]+1;
			else s[j]=s[u]-1;
			dfs1(j,u);
			siz[u]+=siz[j];
			if(siz[j]>siz[son[u]])son[u]=j;
		}
	}
	void dfs2(int u,int f){
		top[u]=f;
		dfn[u]=++tot;
		nw[tot]=s[u];
		if(son[u])dfs2(son[u],f);
		for(auto j:e[u]){
			if(j==fa[u]||j==son[u])continue;
			dfs2(j,j);
		}
	}
	int get_lca(int a,int b){
		while(top[a]!=top[b]){
			if(dep[top[a]]<dep[top[b]])swap(a,b);
			a=fa[top[a]];
		}
		return dep[a]<dep[b]?a:b;
	}
	int get_dist(int a,int b){
		int p=get_lca(a,b);
		return dep[a]+dep[b]-dep[p]*2;
	}
	int tr[N<<2];
	void pushup(int u){
		tr[u]=min(tr[u<<1],tr[u<<1|1]);
	}
	void build(int u,int l,int r){
		if(l==r){
			tr[u]=nw[l];
			return;
		}
		int mid=l+r>>1;
		build(u<<1,l,mid);
		build(u<<1|1,mid+1,r);
		pushup(u);
	}
	int qry(int u,int l,int r,int L,int R){
		if(l>=L&&r<=R)return tr[u];
		int mid=l+r>>1;
		int res=inf;
		if(L<=mid)res=min(res,qry(u<<1,l,mid,L,R));
		if(R>mid)res=min(res,qry(u<<1|1,mid+1,r,L,R));
		return res;
	}
	int qry_path(int a,int b){
		int res=inf;
		while(top[a]!=top[b]){
			if(dep[top[a]]<dep[top[b]])swap(a,b);
			res=min(res,qry(1,1,tot,dfn[top[a]],dfn[a]));
			a=fa[top[a]];
		}
		if(dep[a]<dep[b])swap(a,b);
		res=min(res,qry(1,1,tot,dfn[b],dfn[a]));
		return res;
	}
}tc;
struct vt{
	struct node{
		int b,c,d;
	};
	int stk[N],s[N],top,f[N],res;
	vector<node>e[N];
	vector<int>all;
	void add(int a,int b,int c,int d){
		all.push_back(a);
		all.push_back(b);
		e[a].push_back({b,c,d});
		e[b].push_back({a,c,d});
	}
	void clear(){
		for(auto it:all){
			e[it].clear();
		}
		all.clear();
	}
	void build(int id){
		top=0;
		stk[++top]=0;
		for(auto it:v[id]){
			stk[++top]=it;
		}
		sort(stk+1,stk+top+1,[&](int x,int y){
			return tc.dfn[x]<tc.dfn[y];
		});
		int m=unique(stk+1,stk+top+1)-stk-1;
		top=1;
		s[top]=stk[1];
		for(int i=2;i<=m;i++){
			int u=stk[i],l=tc.get_lca(s[top],u);
			while(top>1&&tc.dep[s[top-1]]>=tc.dep[l]){
				add(s[top-1],s[top],tc.get_dist(s[top-1],s[top]),tc.qry_path(s[top-1],s[top]));
				top--;
			}
			if(s[top]!=l){
				add(l,s[top],tc.get_dist(l,s[top]),tc.qry_path(l,s[top]));
				s[top]=l;
			}
			s[++top]=u;
		}
		while(top>1){
			add(s[top-1],s[top],tc.get_dist(s[top-1],s[top]),tc.qry_path(s[top-1],s[top]));
			top--;
		}
	}
	void dfs(int u,int fa,int val){
		f[u]=0;
		for(auto it:e[u]){
			int j=it.b,w=it.c,v=it.d;
			if(j==fa)continue;
			dfs(j,u,val);
			if(tc.s[u]==val&&v>=val)res=max(res,f[j]+w);
			if(v>=val)f[u]+=f[j]+w;
		}
	}
	void solve(){
		for(int i=0;i<=n+n;i++){
			if(v[i].size()<=1)continue;
			res=-1;
			build(i);
			dfs(0,0,i-n);
			cur=max(cur,res);
			clear();
		}
	}
}vt;
void solve(int cs){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>c[i];
	}
	add(0,1);add(1,0);
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);add(b,a);
	}
	tc.dfs1(0,0);
	tc.dfs2(0,0);
	tc.build(1,1,tc.tot);
	for(int i=0;i<=n;i++){
		v[tc.s[i]+n].push_back(i);
	}
	vt.solve();
	cout<<cur<<'\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//	cin>>T;
//	init();
	for(int cs=1;cs<=T;cs++){
		solve(cs);
	}
	return 0;
}
posted @ 2025-04-15 20:26  zxh923  阅读(9)  评论(0)    收藏  举报