和【洛谷5115】挺像的.
都是统计信息的时候是包含两个树的,那就在一个树上边分治,另一个树上跑一个虚树dp就好了.
式子这么拆:
$dep[i]+dep[j]-(dep[LCA(i,j)]+dep'[LCA'(i,j)]$
$\Rightarrow dep[i]+dep[j]-\frac{1}{2}(dep[i]+dep[j]-dis(i,j)]-dep'[LCA'(i,j)])$
$\Rightarrow \frac{1}{2}(dep[i]+dep[j]+dis(i,j)-2dep'[LCA'(i,j)])$
其中 $dis(i,j)$ 可以拆成 $dis(i,u)+dis(j,v)+val(u,v)$,然后这个就用边分治来跑,把关键点扔到虚树上就行了 .
code:
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <vector>
#define N 1000002
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int n;
ll ans=-inf,W;
namespace IO {
void setIO(string s)
{
string in=s+".in";
string out=s+".out";
freopen(in.c_str(),"r",stdin);
}
};
namespace tree2 {
vector<int>G[N],clr;
ll dis[N];
int edges,t,sta,tot;
int hd[N],to[N<<1],nex[N<<1],dep[N];
ll val[N<<1];
int size[N],son[N],top[N],fa[N],dfn[N],re[N];
int S[N],col[N];
ll value[N],max1[N],max2[N];
void add(int u,ll v,int c)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c;
}
bool cmp(int a,int b)
{
return dfn[a]<dfn[b];
}
void add_vir(int u,int v)
{
G[u].push_back(v);
}
void dfs1(int u,int ff)
{
fa[u]=ff,size[u]=1,dfn[u]=++t;
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dep[v]=dep[u]+1;
dis[v]=dis[u]+val[i];
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(int i=hd[u];i;i=nex[i])
if(to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]);
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
void newnode(int x,ll v,int c)
{
re[++tot]=x;
col[x]=c;
value[x]=v;
}
void Insert(int x)
{
if(sta<=1)
{
S[++sta]=x;
return;
}
int lca=LCA(x,S[sta]);
if(S[sta]==lca) S[++sta]=x;
else
{
while(sta>1&&dep[S[sta-1]]>=dep[lca]) add_vir(S[sta-1],S[sta]),--sta;
if(S[sta]==lca) S[++sta]=x;
else
{
add_vir(lca,S[sta]);
S[sta]=lca;
S[++sta]=x;
}
}
}
void DP(int x)
{
clr.push_back(x);
max1[x]=max2[x]=-100000000000000000;
for(int i=0;i<G[x].size();++i)
{
int y=G[x][i];
DP(y);
ans=max(ans,max1[x]+max2[y]+W-dis[x]*2);
ans=max(ans,max2[x]+max1[y]+W-dis[x]*2);
max1[x]=max(max1[x],max1[y]);
max2[x]=max(max2[x],max2[y]);
}
if(col[x]==1) ans=max(ans,value[x]+max2[x]+W-dis[x]*2),max1[x]=max(max1[x],value[x]);
if(col[x]==2) ans=max(ans,value[x]+max1[x]+W-dis[x]*2),max2[x]=max(max2[x],value[x]);
}
void solve()
{
sort(re+1,re+1+tot,cmp);
if(re[1]!=1) S[++sta]=1;
for(int i=1;i<=tot;++i) Insert(re[i]);
while(sta>1) add_vir(S[sta-1],S[sta]),--sta;
DP(1);
for(int i=0;i<clr.size();++i)
{
int x=clr[i];
max1[x]=max2[x]=value[x]=col[x]=0;
G[x].clear();
}
clr.clear();
tot=sta=0;
}
void Initialize()
{
dfs1(1,0);
dfs2(1,1);
}
};
namespace tree1 {
int tot,totsize,rt1,rt2,ed,mx;
int edges1=1,edges2=1;
int hd[N],pre[N<<1],vis[N<<2],size[N<<1];
ll dis[N<<1];
struct Edge {
int to;
ll w;
int nex;
}e[N<<2],edge[N<<2];
void add(int u,ll v,int c)
{
e[++edges1].nex=pre[u],pre[u]=edges1,e[edges1].to=v,e[edges1].w=c;
}
void add_div(int u,ll v,int c)
{
edge[++edges2].nex=hd[u],hd[u]=edges2,edge[edges2].to=v,edge[edges2].w=c;
}
void getdis(int u,int ff)
{
for(int i=pre[u];i;i=e[i].nex)
{
int v=e[i].to;
if(v==ff) continue;
dis[v]=dis[u]+e[i].w;
getdis(v,u);
}
}
void Build_Tree(int u,int fa)
{
int ff=0;
for(int i=pre[u];i;i=e[i].nex)
{
int v=e[i].to;
if(v==fa) continue;
if(!ff)
{
ff=u;
add_div(u,v,e[i].w);
add_div(v,u,e[i].w);
}
else
{
++tot;
add_div(ff,tot,0);
add_div(tot,ff,0);
add_div(tot,v,e[i].w);
add_div(v,tot,e[i].w);
ff=tot;
}
Build_Tree(v,u);
}
}
void find_edge(int x,int fa)
{
size[x]=1;
for(int i=hd[x];i;i=edge[i].nex)
{
int y=edge[i].to;
if(y==fa||vis[i]) continue;
find_edge(y,x);
int now=max(size[y],totsize-size[y]);
if(now<mx)
{
mx=now;
rt1=y;
rt2=x;
ed=i;
}
size[x]+=size[y];
}
}
void dfs(int u,int ff,ll d,int ty)
{
if(u<=n)
{
tree2::newnode(u,dis[u]+d,ty);
}
for(int i=hd[u];i;i=edge[i].nex)
{
int v=edge[i].to;
if(v==ff||vis[i]) continue;
dfs(v,u,d+edge[i].w,ty);
}
}
void Divide_And_conquer(int x)
{
if(totsize==1) return;
rt1=rt2=ed=0,mx=inf;
find_edge(x,0);
vis[ed]=vis[ed^1]=1;
W=edge[ed].w;
dfs(rt1,0,0,1);
dfs(rt2,0,0,2);
tree2::solve();
int sizert1=size[rt1],sizert2=totsize-size[rt1];
int tmprt1=rt1,tmprt2=rt2;
totsize=sizert1;
Divide_And_conquer(tmprt1);
totsize=sizert2;
Divide_And_conquer(tmprt2);
}
};
int main()
{
// IO::setIO("input");
int i,j;
scanf("%d",&n);
for(i=1;i<n;++i)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
tree1::add(x,y,(ll)z);
tree1::add(y,x,(ll)z);
}
for(i=1;i<n;++i)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
tree2::add(x,y,(ll)z);
tree2::add(y,x,(ll)z);
}
tree2::Initialize();
tree1::tot=n;
tree1::getdis(1,0);
tree1::Build_Tree(1,0);
tree1::totsize=tree1::tot;
tree1::Divide_And_conquer(1);
ans>>=1;
for(i=1;i<=n;++i) ans=max(ans,tree1::dis[i]-tree2::dis[i]);
printf("%lld\n",ans);
return 0;
}