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;
}