#边分治,虚树,欧拉序,LCA#洛谷 4220 [WC2018] 通道
分析
实际上就是要找到一对 \((x,y)\) 使得 \(d1(x,y)+d2(x,y)+d3(x,y)\) 最大。
考虑对第一棵树三度化之后进行边分治,那么 \(d1(x,y)=d1[x]+d1[y]+w\),
如果第二棵树和第三棵树均以 \(1\) 为根,那么求的值转化为 \(d1[x]+d2[x]+d3[x]+d1[y]+d2[y]+d3[y]+w-2*d2[lca(x,y)]-2*d3[lca(x,y)]\)
也就是求 \(d[x]+d[y]+w-2*d2[lca(x,y)]-2*d3[lca(x,y)]\)
对于第二棵树建立虚树,边分治的左半部分染白,右半部分染黑,那么答案相当于是两棵子树在枚举 lca 时的黑白配对。
由于边权非负,固定 \(w-2*d2[lca(x,y)]\) 后相当于对 \(d[x]+d[y]-2*d3[lca(x,y)]\) 求最大值,可以认为是 \(x\) 和 \(x'\) 有一条边权为 \(d1[x]+d2[x]\) 的虚拟边。
相当于是直径的两个端点,而边权非负保证了直径端点的可合并性,那么维护子树内直径的两个端点就能求出答案。
代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N=200011; typedef long long lll; lll s[N],dis[N],d[N],ans; struct node{int y; lll w; int next;}e[N<<1],E[N]; vector<pair<int,lll> >G[N];
int n,lg[N],two[N],deg[N],ext,et=1,as[N],SIZ,vis[N<<1],hs[N],dfn[N],nfd[N],siz[N],col[N],Et=1,part,edge,len[2],dep[N],f[N][18],tot,q[2][N];
int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
int lca(int x,int y){
int l=dfn[x],r=dfn[y];
if (l>r) l^=r,r^=l,l^=r;
int z=lg[r-l+1];
return MIN(f[l][z],f[r-two[z]+1][z]);
}
lll dist(int x,int y){return s[x]+s[y]-2*dis[lca(x,y)];}
struct rec{
int x,y; lll d;
rec operator +(const rec &t)const{
rec a=(rec){x,y,d},b=t,ans=a.d>b.d?a:b;
if (a.x&&b.x){
lll D=dist(a.x,b.x);
if (ans.d<D) ans.d=D,ans.x=a.x,ans.y=b.x;
}
if (a.x&&b.y){
lll D=dist(a.x,b.y);
if (ans.d<D) ans.d=D,ans.x=a.x,ans.y=b.y;
}
if (a.y&&b.x){
lll D=dist(a.y,b.x);
if (ans.d<D) ans.d=D,ans.x=a.y,ans.y=b.x;
}
if (a.y&&b.y){
lll D=dist(a.y,b.y);
if (ans.d<D) ans.d=D,ans.x=a.y,ans.y=b.y;
}
return ans;
}
};
lll calc(rec x,rec y){
lll ans=0;
if (x.x&&y.x) ans=max(ans,dist(x.x,y.x));
if (x.x&&y.y) ans=max(ans,dist(x.x,y.y));
if (x.y&&y.x) ans=max(ans,dist(x.y,y.x));
if (x.y&&y.y) ans=max(ans,dist(x.y,y.y));
return ans;
}
struct Virtual_Tree{
node e[N],E[N]; lll dis[N],wdiv; rec dp[N][2];
int v[N],a[N],vst[N],vtop,nfd[N],dfn[N],dep[N],tot,f[N][18],as[N],hs[N],et=1,Et;
int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
void Pro_st(){
for (int i=1;i<=tot;++i) f[i][0]=nfd[i];
for (int j=1;j<=lg[tot];++j)
for (int i=1;i+two[j]-1<=tot;++i)
f[i][j]=MIN(f[i][j-1],f[i+two[j-1]][j-1]);
}
int lca(int x,int y){
int l=dfn[x],r=dfn[y];
if (l>r) l^=r,r^=l,l^=r;
int z=lg[r-l+1];
return MIN(f[l][z],f[r-two[z]+1][z]);
}
void add(int x,int y,lll w){E[++Et]=(node){y,w,hs[x]},hs[x]=Et;}
void Insert(int x){
if (!vtop) {vst[++vtop]=x; return;}
int Lca=lca(x,vst[vtop]);
for (;vtop>1&&dep[Lca]<dep[vst[vtop-1]];--vtop)
add(vst[vtop-1],vst[vtop],dis[vst[vtop]]-dis[vst[vtop-1]]);
if (dep[Lca]<dep[vst[vtop]]) add(Lca,vst[vtop],dis[vst[vtop]]-dis[Lca]),--vtop;
if (vst[vtop]!=Lca) vst[++vtop]=Lca;
vst[++vtop]=x;
}
void dfs(int x){
if (v[x]){
dp[x][col[x]]=(rec){x,x,dist(x,x)};
dp[x][col[x]^1]=(rec){0,0,0};
}else dp[x][0]=dp[x][1]=(rec){0,0,0};
for (int i=hs[x];i;i=E[i].next){
dfs(E[i].y);
ans=max(ans,calc(dp[x][0],dp[E[i].y][1])-2*dis[x]+wdiv);
ans=max(ans,calc(dp[x][1],dp[E[i].y][0])-2*dis[x]+wdiv);
dp[x][0]=dp[x][0]+dp[E[i].y][0];
dp[x][1]=dp[x][1]+dp[E[i].y][1];
}
hs[x]=0;
}
void doit(){
int m=len[0]+len[1];
for (int i=1;i<=len[0];++i) a[i]=q[0][i];
for (int i=1;i<=len[1];++i) a[i+len[0]]=q[1][i];
for (int i=1;i<=m;++i) v[a[i]]=1;
vtop=Et=0,a[++m]=1;
sort(a+1,a+1+m,[this](int x,int y){return dfn[x]<dfn[y];});
m=unique(a+1,a+1+m)-a-1;
for (int i=1;i<=m;++i) Insert(a[i]);
for (;vtop>1;--vtop) add(vst[vtop-1],vst[vtop],dis[vst[vtop]]-dis[vst[vtop-1]]);
dfs(1);
for (int i=1;i<=m;++i) v[a[i]]=0;
}
void dfs(int x,int fa){
nfd[++tot]=x,dfn[x]=tot,dep[x]=dep[fa]+1,s[x]+=dis[x];
for (int i=as[x];i;i=e[i].next) if (e[i].y!=fa)
dis[e[i].y]=dis[x]+e[i].w,dfs(e[i].y,x),nfd[++tot]=x;
}
}Tre;
void ADD(int x,int y,lll w){
e[++et]=(node){y,w,as[x]},as[x]=et;
e[++et]=(node){x,w,as[y]},as[y]=et;
}
void DFS(int x,int fa){
if (deg[x]>(x!=1)+2){
int lst=x,D=deg[x]-(x!=1);
for (pair<int,lll> t:G[x])
if (t.first!=fa){
if (D<3) ADD(lst,t.first,t.second);
else{
ADD(lst,t.first,t.second);
ADD(lst,++ext,0),lst=ext;
}
DFS(t.first,x),--D;
}
}else{
for (pair<int,lll> t:G[x]) if (t.first!=fa)
ADD(x,t.first,t.second),DFS(t.first,x);
}
}
void Dfs(int x,int fa){
siz[x]=1;
for (int i=as[x];i;i=e[i].next)
if (e[i].y!=fa&&!vis[i]){
Dfs(e[i].y,x),siz[x]+=siz[e[i].y];
int t=max(siz[e[i].y],SIZ-siz[e[i].y]);
if (t<part) part=t,edge=i;
}
}
void dfscol(int x,int fa,int Col){
if (x<=n) q[Col][++len[Col]]=x,col[x]=Col;
for (int i=as[x];i;i=e[i].next)
if (e[i].y!=fa&&!vis[i]) d[e[i].y]=d[x]+e[i].w,dfscol(e[i].y,x,Col);
}
void Dp(int x){
if (SIZ<2) return;
part=SIZ,Dfs(x,0),vis[edge]=vis[edge^1]=1;
int x1=e[edge].y,x2=e[edge^1].y,part1=siz[x1],part2=SIZ-siz[x1];
len[0]=len[1]=d[x1]=d[x2]=0,Tre.wdiv=e[edge].w;
dfscol(x1,x2,0),dfscol(x2,x1,1);
if (!len[0]&&!len[1]) return;
else if (!len[0]&&len[1]) SIZ=part2,Dp(x2);
else if (len[0]&&!len[1]) SIZ=part1,Dp(x1);
else{
for (int i=1;i<=len[0];++i) s[q[0][i]]+=d[q[0][i]];
for (int i=1;i<=len[1];++i) s[q[1][i]]+=d[q[1][i]];
Tre.doit();
for (int i=1;i<=len[0];++i) s[q[0][i]]-=d[q[0][i]];
for (int i=1;i<=len[1];++i) s[q[1][i]]-=d[q[1][i]];
SIZ=part1,Dp(x1),SIZ=part2,Dp(x2);
}
}
void dfs(int x,int fa){
nfd[++tot]=x,dfn[x]=tot,dep[x]=dep[fa]+1,s[x]+=dis[x];
for (int i=hs[x];i;i=E[i].next) if (E[i].y!=fa)
dis[E[i].y]=dis[x]+E[i].w,dfs(E[i].y,x),nfd[++tot]=x;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,ext=n,lg[0]=-1,two[0]=1;
for (int i=1;i<N;++i) lg[i]=lg[i>>1]+1;
for (int i=1;i<=lg[N-1];++i) two[i]=two[i-1]<<1;
for (int i=1;i<n;++i){
int x,y; lll w; cin>>x>>y>>w;
G[x].push_back(make_pair(y,w)),++deg[x];
G[y].push_back(make_pair(x,w)),++deg[y];
}
for (int i=1;i<n;++i){
int x,y; lll w; cin>>x>>y>>w;
Tre.e[++Tre.et]=(node){y,w,Tre.as[x]},Tre.as[x]=Tre.et;
Tre.e[++Tre.et]=(node){x,w,Tre.as[y]},Tre.as[y]=Tre.et;
}
for (int i=1;i<n;++i){
int x,y; lll w; cin>>x>>y>>w;
E[++Et]=(node){y,w,hs[x]},hs[x]=Et;
E[++Et]=(node){x,w,hs[y]},hs[y]=Et;
}
dfs(1,0),DFS(1,0),Tre.dfs(1,0),Tre.Pro_st();
for (int i=1;i<=tot;++i) f[i][0]=nfd[i];
for (int j=1;j<=lg[tot];++j)
for (int i=1;i+two[j]-1<=tot;++i)
f[i][j]=MIN(f[i][j-1],f[i+two[j-1]][j-1]);
SIZ=ext,Dp(1);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号