[WC2018]通道——边分治+虚树+树形DP
题目链接:
题目大意:给出三棵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); }