【学习笔记】[WC2018]通道

21 篇文章 0 订阅

注意到这一点:边权非负

首先对第一棵树进行边分治。式子变成了 D ( x ) + D ( y ) + dist2 ( x , y ) + dist3(x,y) D(x)+D(y)+\text{dist2}(x,y)+\text{dist3(x,y)} D(x)+D(y)+dist2(x,y)+dist3(x,y),显然可以对第二颗树继续分治,但是这样复杂度 O ( n log ⁡ 3 n ) O(n\log^3 n) O(nlog3n)难以通过。

假设 x , y x,y x,y在第二颗树上的最近公共祖先为 p p p,我们考虑对于 x ′ x' x x x x之间连一条边,权值为 D ( x ) + dep2(x) D(x)+\text{dep2(x)} D(x)+dep2(x),那么可以转化为求第三棵树上的最远点对问题。

想到了什么?没错,在一棵树上两个集合并集的最远点对是可以直接合并的。

那么直接在第二棵树的虚树上统计答案即可。

复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define db double
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=8e5+5;
int T,n,n2;
int dep[N],dep2[N],vis[N],dfn[N],tp[N],tp2[N],fa[N],fa2[N],son[N],son2[N],siz2[N],siz3[N],num,c[N];
int hd[N],to[N],st[N],nxt[N],tot=1;
ll dis1[N],dis2[N],dis3[N],w[N],res;
vector<pair<int,ll>>g[N];
void add(int x,int y,ll z){
	to[++tot]=y,w[tot]=z,st[tot]=x,nxt[tot]=hd[x],hd[x]=tot;
	to[++tot]=x,w[tot]=z,st[tot]=y,nxt[tot]=hd[y],hd[y]=tot;
}
int Lca(int x,int y){
	int fx=tp[x],fy=tp[y];
	while(fx!=fy){
		if(dep[fx]>dep[fy])x=fa[fx];
		else y=fa[fy];
		fx=tp[x],fy=tp[y];
	}return dep[x]<dep[y]?x:y;
}
int Lca2(int x,int y){
	int fx=tp2[x],fy=tp2[y];
	while(fx!=fy){
		if(dep2[fx]>dep2[fy])x=fa2[fx];
		else y=fa2[fy];
		fx=tp2[x],fy=tp2[y];
	}return dep2[x]<dep2[y]?x:y;
}
void dfs(int u,int topf){
	int lst=u;
	for(auto v:g[u]){
		if(v.fi!=topf){
			n2++,add(lst,n2,0),add(n2,v.fi,v.se),lst=n2,dfs(v.fi,u);
		}
	}
}
void dfs2(int u,int topf){
	dfn[u]=++num,fa[u]=topf,dep[u]=dep[topf]+1,siz2[u]=1;
	for(auto v:g[u]){
		if(v.fi!=topf){
			dis2[v.fi]=dis2[u]+v.se,dfs2(v.fi,u),siz2[u]+=siz2[v.fi];
			if(siz2[v.fi]>siz2[son[u]])son[u]=v.fi;
		}
	}
}
void dfs7(int u,int topf){
	tp[u]=topf;
	if(son[u])dfs7(son[u],topf);
	for(auto v:g[u]){
		if(!tp[v.fi])dfs7(v.fi,v.fi);
	}
}
void dfs3(int u,int topf){
	fa2[u]=topf,dep2[u]=dep2[topf]+1,siz3[u]=1;
	for(auto v:g[u]){
		if(v.fi!=topf){
			dis3[v.fi]=dis3[u]+v.se,dfs3(v.fi,u),siz3[u]+=siz3[v.fi];
			if(siz3[v.fi]>siz3[son2[u]])son2[u]=v.fi;
		}
	}
}
void dfs8(int u,int topf){
	tp2[u]=topf;
	if(son2[u])dfs8(son2[u],topf);
	for(auto v:g[u]){
		if(!tp2[v.fi])dfs8(v.fi,v.fi);
	}
}
int edge,D,siz[N];
void dfs4(int u,int topf,int sz){
	siz[u]=1;
	for(int k=hd[u];k;k=nxt[k]){
		int v=to[k];
		if(!vis[k]&&v!=topf){
			dfs4(v,u,sz),siz[u]+=siz[v];
			if(max(siz[v],sz-siz[v])<D)D=max(siz[v],sz-siz[v]),edge=k;
		}
	}
}
int m,p[N],s[N],cnt;
vector<int>vec,g2[N];
void dfs5(int u,int topf,int C){
	c[u]=C;if(u<=n)p[++m]=u;
	for(int k=hd[u];k;k=nxt[k]){
		int v=to[k];
		if(!vis[k]&&v!=topf){
			dis1[v]=dis1[u]+w[k],dfs5(v,u,C);
		}
	}
}
bool cmp(int x,int y){
	return dfn[x]<dfn[y];
}
void Add(int x,int y){
	if(dep[x]>dep[y])swap(x,y);
	g2[x].pb(y);
}
void build(){
	for(int i=1;i<=m;i++)vec.pb(p[i]);
	s[cnt=1]=1,vec.pb(1);sort(p+1,p+1+m,cmp);
	for(int i=1;i<=m;i++){
		if(p[i]==1)continue;
		int u=p[i],v=Lca(s[cnt],u),lst=0;
		if(s[cnt]==v)s[++cnt]=u;
		else{
			while(dep[s[cnt]]>dep[v]){
				if(lst)Add(s[cnt],lst);
				lst=s[cnt--];
			}
			if(s[cnt]==v){
				Add(lst,v),s[++cnt]=u;
			}
			else{
				Add(lst,v),s[++cnt]=v,vec.pb(v),s[++cnt]=u;
			}
		}
	}for(int i=1;i<cnt;i++)Add(s[i],s[i+1]);
}
ll ask(int x,int y){
	if(!x||!y||x==y)return -inf;
	return dis3[x]+dis3[y]-2*dis3[Lca2(x,y)]+dis1[x]+dis1[y]+dis2[x]+dis2[y]+w[edge];
}
struct node{
	int a,b;
	bool operator <(const node &r)const{
		return ask(a,b)<ask(r.a,r.b);
	}
	node operator +(const node &r)const{
		if(!a||!r.a)return {a+r.a,b+r.b};
		return max({(node){a,r.a},(node){a,r.b},(node){b,r.a},(node){b,r.b},(node){a,b},r});
	}
}dp[N],dp2[N];
void dfs6(int u){
	dp[u]=dp2[u]={0,0};
	if(c[u]==1)dp[u]={u,u};
	if(c[u]==2)dp2[u]={u,u};
	for(auto v:g2[u]){
		dfs6(v);
		ll tmp=max({ask(dp[u].a,dp2[v].a),ask(dp[u].a,dp2[v].b),ask(dp[u].b,dp2[v].a),ask(dp[u].b,dp2[v].b)});
		ll tmp2=max({ask(dp2[u].a,dp[v].a),ask(dp2[u].a,dp[v].b),ask(dp2[u].b,dp[v].a),ask(dp2[u].b,dp[v].b)});
		res=max(res,max(tmp,tmp2)-2*dis2[u]);
		dp[u]=dp[u]+dp[v],dp2[u]=dp2[u]+dp2[v];
	}
}
void solve(int u,int sz){
	if(sz==1)return;
	edge=-1,D=0x3f3f3f3f;
	dfs4(u,0,sz);vis[edge]=vis[edge^1]=1;
	dis1[st[edge]]=dis1[to[edge]]=0,m=0,dfs5(st[edge],0,1),dfs5(to[edge],0,2);
	build(),dfs6(1);
	for(int i=0;i<vec.size();i++)c[vec[i]]=0,g2[vec[i]].clear();vec.clear();
	int tmp=edge;
	solve(st[tmp],sz-siz[to[tmp]]),solve(to[tmp],siz[to[tmp]]);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n,n2=n;
	for(int i=1;i<n;i++){
		int x,y;ll z;cin>>x>>y>>z;
		g[x].pb({y,z}),g[y].pb({x,z});
	}dfs(1,0);for(int i=1;i<=n;i++)g[i].clear();
	for(int i=1;i<n;i++){
		int x,y;ll z;cin>>x>>y>>z;
		g[x].pb({y,z}),g[y].pb({x,z});
	}dfs2(1,0),dfs7(1,1);for(int i=1;i<=n;i++)g[i].clear(),son[i]=0;
	for(int i=1;i<n;i++){
		int x,y;ll z;cin>>x>>y>>z;
		g[x].pb({y,z}),g[y].pb({x,z});
	}dfs3(1,0),dfs8(1,1);for(int i=1;i<=n;i++)g[i].clear();
	solve(1,n2);cout<<res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值