[WC2018]通道——边分治+虚树+树形DP

题目链接:

[WC2018]通道

 

题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和。

我们按照部分分逐个分析有1、2、3棵树时的做法。

首先说一个结论,在下面讲解中能应用到:

对于一棵树T1的直径两端点为u,v,对于另一棵树T2的直径两端点为x,y,如果将两棵树合并(即将两棵树中的各一个点连边)那么新树的直径的两端点一定是u,v,x,y中的两个。

证明见树的直径及其性质与证明

一、一棵树

这个很好做吧,直接求树的直径就好了。

二、两棵树

我们假设答案点对是(a,b),那么ans(a,b)=dis1(a,b)+dis2(a,b)。其中dis1,dis2分别表示两棵树上的两点距离。

我们将第一棵树的答案拆开表示:ans(a,b)=dep1(a)+dep1(b)-2*dep1(lca)+dis2(a,b)。其中dep1为第一棵树中的该点深度。

可以发现dep1(lca)与a,b无关,我们对于第二棵树上的点x建立一个点x'与x相连,边权为dep1(x)。

这样忽略dep1(lca),答案就是Tree2中的直径。

现在计算dep1(lca)对答案的影响,我们可以对于Tree1树形DP,每个点存子树中所有点在Tree2中的形成的直径的两端点及直径长度,回溯时将每个子节点的答案合并到父节点上,这时Tree2中的直径减掉这个父节点深度*2即可更新答案。合并时利用上面讲到的结论六种情况枚举讨论,更新答案时因为要保证直径两端点不在这个父节点的同一棵子树内,所以有四种情况可以更新答案。这里Tree1中一个点子树中所有点在Tree2中形成的直径可以看作是这些点在Tree2上两两之间路径包含的所有点组成的树的直径,而合并时相当于在Tree2上将两个可能有交集的子树拼接到一起,上述结论依旧成立。

时间复杂度O(nlogn)。

三、三棵树

三棵树时答案为ans(a,b)=dis1(a,b)+dis2(a,b)+dis3(a,b)。

我们对于第一棵树进行边分治,将多叉树转二叉树,对于每次分治的联通块,以中心边为界将联通块分成两部分。(边分治具体实现参见边分治讲解

同时将第二棵树的答案拆开表示:ans(a,b)=d1(a)+d1(b)+val+dep2(a)+dep2(b)-2*dep2(lca)+dis3(a,b)。其中d1为该点到当前分治中心边的距离,val为分治中心边的长度。

同样按照两棵树时的做法,对于第三棵树上的点x建一个点x'与x相连,边权为d1(x)+dep2(x)。

每次对Tree1进行边分治时将分治联通块中的点在Tree2上建虚树,按照两棵树时的做法在虚树上树形DP。

因为还需要保证在Tree3中找到的直径的端点在Tree1中分别位于分治中心边的两端,所以每次边分治将分治中心一边的点标号为1,另一边的点标号为2。

在树形DP是每个点分别维护子树中标号为1/2的点组成的直径的两端点及直径长度,合并同样各种情况讨论一下并更新答案即可。

时间复杂度为O(nlogn^2),RMQ求LCA+基数排序可以将时间复杂度降为O(nlogn)。

注意:因为边权可能为0,所以求lca时不能比较真实深度(即带权深度)而要比较不带权深度。

这道题也可以用点分治来代替边分治,但边分治能将每次分治联通块中的点恰好分成两部分,而点分治对于分治中心的处理比较麻烦,所以建议写边分治。

亲测用树剖求LCA比用RMQ求LCA快,也不知道为什么...

调了好几天,也写了好几个版本的,最后还是都坚持调出来了,虽然很长,但还是都放上来,供读者选择。

其中edge_partation为第一棵树即边分治的树;virtual_tree为第二棵树即建虚树的树;value_tree为第三棵树即需要求直径的树。

RMQ求LCA+非递归树形DP

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define pr pair<int,ll>
#define INF 1<<30
int n,m;
int cnt;
int x,y;
ll ans,z;
ll lty[100010];
int col[100010];
struct Miku
{
	int x;
	ll dep;
}t[400010];
namespace value_tree
{
	int tot;
	int dfn=0;
	ll d[100010];
	int s[100010];
	ll val[200010];
	int lg[200010];
	int to[200010];
	int head[100010];
	int next[200010];
	ll f[200010][19];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		f[++dfn][0]=d[x];
		s[x]=dfn;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				d[to[i]]=d[x]+val[i];
				dfs(to[i],x);
				f[++dfn][0]=d[x];
			}
		}
	}
	inline void ST()
	{
		for(int i=2;i<=dfn;i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		for(int j=1;j<=18;j++)
		{
			for(int i=1;i+(1<<j)-1<=dfn;i++)
			{
				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			}
		}
	}
	inline ll lca(int x,int y)
	{
		x=s[x],y=s[y];
		if(x>y)
		{
			swap(x,y);
		}
		int len=lg[y-x+1];
		return min(f[x][len],f[y-(1<<len)+1][len]);
	}
	inline ll dis(int x,int y)
	{
		return lty[x]+lty[y]-(lca(x,y)<<1);
	}
}
namespace virtual_tree
{
	int tot;
	int num;
	int top;
	int dfn=0;
	int sum=0;
	ll mid_edge;
	ll d[100010];
	int s[100010];
	int l[100010];
	int r[100010];
	int q[200010];
	ll val[200010];
	int lg[200010];
	int to[200010];
	int st[200010];
	int dep[100010];
	int vis[100010];
	int head[100010];
	int next[200010];
	int f[200010][19];
	struct miku
	{
		int u,v;
		ll len;
		miku(){u=0,v=0,len=0;}
		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
		miku (const int& U,const int& V,const ll& L){u=U,v=V,len=L;}
		friend bool operator <(miku a,miku b){return a.len<b.len;}
		friend miku operator +(miku a,miku b)
		{
			if(a.u==0)return b;
			if(b.u==0)return a;
			miku res=max(a,b);
			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
			return res;
		}
	}dp[100010][2];
	bool cmp(int a,int b)
	{
		int x=a<0?r[-a]:l[a];
		int y=b<0?r[-b]:l[b];
		return x<y;
	}
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		f[++dfn][0]=x;
		s[x]=dfn;
		l[x]=++sum;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				dep[to[i]]=dep[x]+1;
				d[to[i]]=d[x]+val[i];
				dfs(to[i],x);
				f[++dfn][0]=x;
			}
		}
		r[x]=++sum;
	}
	inline int mn(int x,int y)
	{
		return dep[x]<dep[y]?x:y;
	}
	inline void ST()
	{
		for(int i=2;i<=dfn;i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		for(int j=1;j<=18;j++)
		{
			for(int i=1;i+(1<<j)-1<=dfn;i++)
			{
				f[i][j]=mn(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			}
		}
	}
	inline int lca(int x,int y)
	{
		x=s[x],y=s[y];
		if(x>y)
		{
			swap(x,y);
		}
		int len=lg[y-x+1];
		return mn(f[x][len],f[y-(1<<len)+1][len]);
	}
	inline ll merge(const miku& a,const miku& b)
	{
		if(a.u==0||b.u==0)return 0;
		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
	}
	void tree_dp()
	{
		top=0;
		for(int i=1;i<=tot;i++)
		{
			if(q[i]>0)
			{
				st[++top]=q[i];
			}
			else
			{
				top--;
				if(!top)continue;
				int fa=st[top];
				int x=st[top+1];
				ans=max(ans,max(merge(dp[x][0],dp[fa][1]),merge(dp[x][1],dp[fa][0]))+mid_edge-(d[fa]<<1));
				dp[fa][0]=dp[fa][0]+dp[x][0];
				dp[fa][1]=dp[fa][1]+dp[x][1];
			}
		}
	}
	inline void build(ll value)
	{
		mid_edge=value;
		for(int i=1;i<=cnt;i++)
		{
			vis[t[i].x]=1;
			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
			q[i]=t[i].x;
			col[t[i].x]=0;
		}
		num=tot=cnt;
		sort(q+1,q+1+tot,cmp);
		for(int i=1;i<num;i++)
		{
			int fa=lca(q[i],q[i+1]);
			if(!vis[fa])
			{
				vis[fa]=1;
				q[++tot]=fa;
				dp[fa][0]=dp[fa][1]=(miku){0,0,0};
			}
		}
		for(int i=1;i<=tot;i++)
		{
			vis[q[i]]=0;
		}
		num=tot;
		for(int i=1;i<=num;i++)
		{
			q[++tot]=-q[i];
		}
		sort(q+1,q+1+tot,cmp);
		tree_dp();
	}
}
namespace edge_partation
{
	int tot;
	int num;
	int root;
	int to[800010];
	ll val[800010];
	int vis[400010];
	int next[800010];
	int head[400010];
	int size[400010];
	vector<pr>q[400010];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				q[x].push_back(make_pair(to[i],val[i]));
				dfs(to[i],x);
			}
		}
	}
	inline void rebuild()
	{
		tot=1;
		memset(head,0,sizeof(head));
		memset(val,0,sizeof(val));
		memset(next,0,sizeof(next));
		memset(to,0,sizeof(to));
		for(int i=1;i<=m;i++)
		{
			int len=q[i].size();
			if(len<=2)
			{
				for(int j=0;j<len;j++)
				{
					add(i,q[i][j].first,q[i][j].second);
					add(q[i][j].first,i,q[i][j].second);
				}
			}
			else
			{
				int ls=++m;
				int rs=++m;
				add(i,ls,0);
				add(ls,i,0);
				add(i,rs,0);
				add(rs,i,0);
				for(int j=0;j<len;j++)
				{
					if(j&1)
					{
						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
					else
					{
						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
				}
			}
		}
	}
	void getroot(int x,int fa,int sum)
	{
		size[x]=1;
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				getroot(to[i],x,sum);
				size[x]+=size[to[i]];
				int mx_size=max(size[to[i]],sum-size[to[i]]);
				if(mx_size<num)
				{
					num=mx_size;
					root=i;
				}
			}
		}
	}
	void dfs2(int x,int fa,ll dep,int opt)
	{
		if(x<=n)
		{
			col[x]=opt;
			t[++cnt]=(Miku){x,dep};
		}
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				dfs2(to[i],x,dep+val[i],opt);
			}
		}
	}
	void partation(int x,int sum)
	{
		num=INF;
		getroot(x,0,sum);
		if(num==INF)
		{
			return ;
		}
		int now=root;
		vis[now>>1]=1;
		cnt=0;
		dfs2(to[now],0,0ll,1);
		dfs2(to[now^1],0,0ll,2);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]+=t[i].dep;
		}
		virtual_tree::build(val[now]);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]-=t[i].dep;
		}
		int sz=size[to[now]];
		partation(to[now],sz);
		partation(to[now^1],sum-sz);
	}
}
int main()
{
	scanf("%d",&n);
	m=n;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		edge_partation::add(x,y,z);
		edge_partation::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		virtual_tree::add(x,y,z);
		virtual_tree::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		value_tree::add(x,y,z);
		value_tree::add(y,x,z);
	}
	virtual_tree::dfs(1,0);
	virtual_tree::ST();
	value_tree::dfs(1,0);
	value_tree::ST();
	edge_partation::dfs(1,0);
	edge_partation::rebuild();
	edge_partation::partation(1,m);
	printf("%lld",ans);
}

RMQ求LCA+递归树形DP

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define pr pair<int,ll>
#define INF 1<<30
int n,m;
int cnt;
int x,y;
ll ans,z;
ll lty[100010];
int col[100010];
struct Miku
{
	int x;
	ll dep;
}t[400010];
namespace value_tree
{
	int tot;
	int dfn=0;
	ll d[100010];
	int s[100010];
	ll val[200010];
	int lg[200010];
	int to[200010];
	int head[100010];
	int next[200010];
	ll f[200010][19];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		f[++dfn][0]=d[x];
		s[x]=dfn;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				d[to[i]]=d[x]+val[i];
				dfs(to[i],x);
				f[++dfn][0]=d[x];
			}
		}
	}
	inline void ST()
	{
		for(int i=2;i<=dfn;i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		for(int j=1;j<=18;j++)
		{
			for(int i=1;i+(1<<j)-1<=dfn;i++)
			{
				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			}
		}
	}
	inline ll lca(int x,int y)
	{
		x=s[x],y=s[y];
		if(x>y)
		{
			swap(x,y);
		}
		int len=lg[y-x+1];
		return min(f[x][len],f[y-(1<<len)+1][len]);
	}
	inline ll dis(int x,int y)
	{
		return lty[x]+lty[y]-(lca(x,y)<<1);
	}
}
namespace virtual_tree
{
	int tot;
	int top;
	int sum=0;
	int dfn=0;
	ll mid_edge;
	ll d[100010];
	int l[100010];
	int s[100010];
	ll val[200010];
	int lg[200010];
	int to[200010];
	int st[100010];
	int vis[100010];
	int dep[100010];
	int head[100010];
	int next[200010];
	int f[200010][19];
	vector<int>q[100010];
	struct miku
	{
		int u,v;
		ll len;
		miku(){u=0,v=0,len=0;}
		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
		miku (const int& U,const int& V,const ll& L){u=U,v=V,len=L;}
		friend bool operator <(miku a,miku b){return a.len<b.len;}
		friend miku operator +(miku a,miku b)
		{
			if(a.u==0)return b;
			if(b.u==0)return a;
			miku res=max(a,b);
			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
			return res;
		}
	}dp[100010][2];
	bool cmp(Miku a,Miku b)
	{
		return l[a.x]<l[b.x];
	}
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		f[++dfn][0]=x;
		s[x]=dfn;
		l[x]=++sum;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				dep[to[i]]=dep[x]+1;
				d[to[i]]=d[x]+val[i];
				dfs(to[i],x);
				f[++dfn][0]=x;
			}
		}
	}
	inline int mn(int x,int y)
	{
		return dep[x]<dep[y]?x:y;
	}
	inline void ST()
	{
		for(int i=2;i<=dfn;i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		for(int j=1;j<=18;j++)
		{
			for(int i=1;i+(1<<j)-1<=dfn;i++)
			{
				f[i][j]=mn(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			}
		}
	}
	inline int lca(int x,int y)
	{
		x=s[x],y=s[y];
		if(x>y)
		{
			swap(x,y);
		}
		int len=lg[y-x+1];
		return mn(f[x][len],f[y-(1<<len)+1][len]);
	}
	inline void insert(int x)
	{
		int fa=lca(x,st[top]);
		if(!vis[fa])
		{
			vis[fa]=1;
			dp[fa][0]=dp[fa][1]=(miku){0,0,0};
		}
		while(top>1&&dep[st[top-1]]>=dep[fa])
        {
            q[st[top-1]].push_back(st[top]);
            top--;
        }
        if(fa!=st[top])
        {
            q[fa].push_back(st[top]);
            st[top]=fa;
        }
        st[++top]=x;
	}
	inline ll merge(const miku& a,const miku& b)
	{
		if(a.u==0||b.u==0)return 0;
		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
	}
	void tree_dp(int x)
	{
		int len=q[x].size();
		for(int i=0;i<len;i++)
		{
			int to=q[x][i];
			tree_dp(to);
			ans=max(ans,max(merge(dp[x][0],dp[to][1]),merge(dp[x][1],dp[to][0]))+mid_edge-(d[x]<<1));
			dp[x][0]=dp[x][0]+dp[to][0];
			dp[x][1]=dp[x][1]+dp[to][1];
		}
		vis[x]=0;
		q[x].clear();
	}
	inline void build(ll value)
	{
		mid_edge=value;
		for(int i=1;i<=cnt;i++)
		{
			vis[t[i].x]=1;
			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
			col[t[i].x]=0;
		}
		sort(t+1,t+1+cnt,cmp);
		top=0;
		if(t[1].x!=1)
		{
			st[++top]=1;
		}
		for(int i=1;i<=cnt;i++)
		{
			insert(t[i].x);
		}
		while(top>1)
		{
			q[st[top-1]].push_back(st[top]);
			top--;
		}
		tree_dp(1);
	}
}
namespace edge_partation
{
	int tot;
	int num;
	int root;
	int to[800010];
	ll val[800010];
	int vis[400010];
	int next[800010];
	int head[400010];
	int size[400010];
	vector<pr>q[400010];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				q[x].push_back(make_pair(to[i],val[i]));
				dfs(to[i],x);
			}
		}
	}
	inline void rebuild()
	{
		tot=1;
		memset(head,0,sizeof(head));
		for(int i=1;i<=m;i++)
		{
			int len=q[i].size();
			if(len<=2)
			{
				for(int j=0;j<len;j++)
				{
					add(i,q[i][j].first,q[i][j].second);
					add(q[i][j].first,i,q[i][j].second);
				}
			}
			else
			{
				int ls=++m;
				int rs=++m;
				add(i,ls,0);
				add(ls,i,0);
				add(i,rs,0);
				add(rs,i,0);
				for(int j=0;j<len;j++)
				{
					if(j&1)
					{
						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
					else
					{
						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
				}
			}
		}
	}
	void getroot(int x,int fa,int sum)
	{
		size[x]=1;
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				getroot(to[i],x,sum);
				size[x]+=size[to[i]];
				int mx_size=max(size[to[i]],sum-size[to[i]]);
				if(mx_size<num)
				{
					num=mx_size;
					root=i;
				}
			}
		}
	}
	void dfs2(int x,int fa,ll dep,int opt)
	{
		if(x<=n)
		{
			col[x]=opt;
			t[++cnt]=(Miku){x,dep};
		}
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				dfs2(to[i],x,dep+val[i],opt);
			}
		}
	}
	void partation(int x,int sum)
	{
		num=INF;
		getroot(x,0,sum);
		if(num==INF)
		{
			return ;
		}
		int now=root;
		vis[now>>1]=1;
		cnt=0;
		dfs2(to[now],0,0ll,1);
		dfs2(to[now^1],0,0ll,2);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]+=t[i].dep;
		}
		virtual_tree::build(val[now]);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]-=t[i].dep;
		}
		int sz=size[to[now]];
		partation(to[now],sz);
		partation(to[now^1],sum-sz);
	}
}
int main()
{
	scanf("%d",&n);
	m=n;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		edge_partation::add(x,y,z);
		edge_partation::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		virtual_tree::add(x,y,z);
		virtual_tree::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		value_tree::add(x,y,z);
		value_tree::add(y,x,z);
	}
	virtual_tree::dfs(1,0);
	virtual_tree::ST();
	value_tree::dfs(1,0);
	value_tree::ST();
	edge_partation::dfs(1,0);
	edge_partation::rebuild();
	edge_partation::partation(1,m);
	printf("%lld",ans);
}

树剖求LCA+递归树形DP

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define pr pair<int,ll>
#define INF 1<<30
int n,m;
int cnt;
int x,y;
ll ans,z;
int lg[200010];
ll lty[100010];
int col[100010];
struct Miku
{
	int x;
	ll dep;
}t[400010];
namespace value_tree
{
	int tot;
	int dfn;
	ll d[100010];
	int s[100010];
	ll val[200010];
	int to[200010];
	int head[100010];
	int next[200010];
	ll f[200010][19];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		f[++dfn][0]=d[x];
		s[x]=dfn;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				d[to[i]]=d[x]+val[i];
				dfs(to[i],x);
				f[++dfn][0]=d[x];
			}
		}
	}
	inline void ST()
	{
		for(int i=2;i<=dfn;i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		for(int j=1;j<=18;j++)
		{
			for(int i=1;i+(1<<j)-1<=dfn;i++)
			{
				f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
			}
		}
	}
	inline ll lca(int x,int y)
	{
		x=s[x],y=s[y];
		if(x>y)
		{
			swap(x,y);
		}
		int len=lg[y-x+1];
		return min(f[x][len],f[y-(1<<len)+1][len]);
	}
	inline ll dis(int x,int y)
	{
		return lty[x]+lty[y]-(lca(x,y)<<1);
	}
}
namespace virtual_tree
{
	int tot;
	int top;
	int num=0;
	ll mid_edge;
	ll d[100010];
	int f[100010];
	int s[100010];
	ll val[200010];
	int to[200010];
	int st[100010];
	int vis[100010];
	int anc[100010];
	int son[100010];
	int dep[100010];
	int size[100010];
	int head[100010];
	int next[200010];
	vector<int>q[100010];
	struct miku
	{
		int u,v;
		ll len;
		miku(){u=0,v=0,len=0;}
		miku (const int& U,const int& V){u=U,v=V,len=value_tree::dis(u,v);}
		miku (const int& U,const int& V,const int& L){u=U,v=V,len=L;}
		friend bool operator <(miku a,miku b){return a.len<b.len;}
		friend miku operator +(miku a,miku b)
		{
			if(a.u==0)return b;
			if(b.u==0)return a;
			miku res=max(a,b);
			res=max(res,max(miku(a.u,b.v),miku(a.v,b.u)));
			res=max(res,max(miku(a.u,b.u),miku(a.v,b.v)));
			return res;
		}
	}dp[100010][2];
	bool cmp(Miku a,Miku b)
	{
		return s[a.x]<s[b.x];
	}
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x)
	{
		dep[x]=dep[f[x]]+1;
		size[x]=1;
		lty[x]+=d[x];
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=f[x])
			{
				f[to[i]]=x;
				d[to[i]]=d[x]+val[i];
				dfs(to[i]);
				size[x]+=size[to[i]];
				if(size[to[i]]>size[son[x]])
				{
					son[x]=to[i];
				}
			}
		}
	}
	void dfs2(int x,int tp)
	{
		s[x]=++num;
		anc[x]=tp;
		if(son[x])
		{
			dfs2(son[x],tp);
		}
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=f[x]&&to[i]!=son[x])
			{
				dfs2(to[i],to[i]);
			}
		}
	}
	inline int lca(int x,int y)
	{
		while(anc[x]!=anc[y])
		{
			if(dep[anc[x]]<dep[anc[y]])
			{
				swap(x,y);
			}
			x=f[anc[x]];
		}
		return dep[x]<dep[y]?x:y;
	}
	inline void insert(int x)
	{
		int fa=lca(x,st[top]);
		if(!vis[fa])
		{
			vis[fa]=1;
			dp[fa][0]=dp[fa][1]=(miku){0,0,0};
		}
		while(top>1&&dep[st[top-1]]>=dep[fa])
		{
			q[st[top-1]].push_back(st[top]);
			top--;
		}
		if(fa!=st[top])
		{
			q[fa].push_back(st[top]);
			st[top]=fa;
		}
		st[++top]=x;
	}
	inline ll merge(const miku& a,const miku& b)
	{
		if(a.u==0||b.u==0)return 0;
		return max(max(value_tree::dis(a.u,b.u),value_tree::dis(a.v,b.u)),max(value_tree::dis(a.u,b.v),value_tree::dis(a.v,b.v)));
	}
	void tree_dp(int x)
	{
		int len=q[x].size();
		for(int i=0;i<len;i++)
		{
			int to=q[x][i];
			tree_dp(to);
			ans=max(ans,max(merge(dp[x][0],dp[to][1]),merge(dp[x][1],dp[to][0]))+mid_edge-2*d[x]);
			dp[x][0]=dp[x][0]+dp[to][0];
			dp[x][1]=dp[x][1]+dp[to][1];
		}
		vis[x]=0;
		q[x].clear();
	}
	inline void build(ll value)
	{
		mid_edge=value;
		for(int i=1;i<=cnt;i++)
		{
			vis[t[i].x]=1;
			dp[t[i].x][col[t[i].x]-1]=(miku){t[i].x,t[i].x,0};
			dp[t[i].x][(col[t[i].x]-1)^1]=(miku){0,0,0};
			col[t[i].x]=0;
		}
		sort(t+1,t+1+cnt,cmp);
		top=0;
		if(t[1].x!=1)
		{
			st[++top]=1;
		}
		for(int i=1;i<=cnt;i++)
		{
			insert(t[i].x);
		}
		while(top>1)
		{
			q[st[top-1]].push_back(st[top]);
			top--;
		}
		tree_dp(1);
	}
}
namespace edge_partation
{
	int tot;
	int num;
	int root;
	int to[800010];
	ll val[800010];
	int vis[400010];
	int next[800010];
	int head[400010];
	int size[400010];
	vector<pr>q[400010];
	inline void add(int x,int y,ll z)
	{
		next[++tot]=head[x];
		head[x]=tot;
		to[tot]=y;
		val[tot]=z;
	}
	void dfs(int x,int fa)
	{
		for(int i=head[x];i;i=next[i])
		{
			if(to[i]!=fa)
			{
				q[x].push_back(make_pair(to[i],val[i]));
				dfs(to[i],x);
			}
		}
	}
	inline void rebuild()
	{
		tot=1;
		memset(head,0,sizeof(head));
		for(int i=1;i<=m;i++)
		{
			int len=q[i].size();
			if(len<=2)
			{
				for(int j=0;j<len;j++)
				{
					add(i,q[i][j].first,q[i][j].second);
					add(q[i][j].first,i,q[i][j].second);
				}
			}
			else
			{
				int ls=++m;
				int rs=++m;
				add(i,ls,0);
				add(ls,i,0);
				add(i,rs,0);
				add(rs,i,0);
				for(int j=0;j<len;j++)
				{
					if(j&1)
					{
						q[ls].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
					else
					{
						q[rs].push_back(make_pair(q[i][j].first,q[i][j].second));
					}
				}
			}
		}
	}
	void getroot(int x,int fa,int sum)
	{
		size[x]=1;
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				getroot(to[i],x,sum);
				size[x]+=size[to[i]];
				int mx_size=max(size[to[i]],sum-size[to[i]]);
				if(mx_size<num)
				{
					num=mx_size;
					root=i;
				}
			}
		}
	}
	void dfs2(int x,int fa,ll dep,int opt)
	{
		if(x<=n)
		{
			col[x]=opt;
			t[++cnt]=(Miku){x,dep};
		}
		for(int i=head[x];i;i=next[i])
		{
			if(!vis[i>>1]&&to[i]!=fa)
			{
				dfs2(to[i],x,dep+val[i],opt);
			}
		}
	}
	void partation(int x,int sum)
	{
		num=INF;
		getroot(x,0,sum);
		if(num==INF)
		{
			return ;
		}
		int now=root;
		vis[now>>1]=1;
		cnt=0;
		dfs2(to[now],0,0ll,1);
		dfs2(to[now^1],0,0ll,2);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]+=t[i].dep;
		}
		virtual_tree::build(val[now]);
		for(int i=1;i<=cnt;i++)
		{
			lty[t[i].x]-=t[i].dep;
		}
		int sz=size[to[now]];
		partation(to[now],sz);
		partation(to[now^1],sum-sz);
	}
}
int main()
{
	scanf("%d",&n);
	m=n;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		edge_partation::add(x,y,z);
		edge_partation::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		virtual_tree::add(x,y,z);
		virtual_tree::add(y,x,z);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&x,&y,&z);
		value_tree::add(x,y,z);
		value_tree::add(y,x,z);
	}
	virtual_tree::dfs(1);
	virtual_tree::dfs2(1,1);
	value_tree::dfs(1,0);
	value_tree::ST();
	edge_partation::dfs(1,0);
	edge_partation::rebuild();
	edge_partation::partation(1,m);
	printf("%lld",ans);
}
posted @ 2018-12-24 14:50  The_Virtuoso  阅读(758)  评论(1编辑  收藏  举报