题目链接:

[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);
}

[WC2018]通道——边分治+虚树+树形DP的更多相关文章

  1. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  2. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  3. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  4. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  5. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  6. LOJ 2339 「WC2018」通道——边分治+虚树

    题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...

  7. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

  8. 【UOJ347】【WC2018】通道 边分治 虚树 DP

    题目大意 给你三棵树,点数都是\(n\).求 \[ \max_{i,j}d_1(i,j)+d_2(i,j)+d_3(i,j) \] 其中\(d_k(i,j)\)是在第\(k\)棵数中\(i,j\)两点 ...

  9. UOJ#347. 【WC2018】通道 边分治 虚树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ347.html 题意 有三棵树,边有边权. 对于所有点对 (x,y) 求在三棵树上 x 到 y 的距离之和 ...

随机推荐

  1. js获取地址栏传参

    地址:http://127.0.0.1:8082/prosperleedir/index.html?id=6666&name=prosper#prosper         Location{ ...

  2. 解决注册并发问题并提高QPS

    前言:前面在本地的windows通过apache的ab工具测试了600并发下“查询指定手机是否存在再提交数据”的注册功能会出现重复提交的情况,并且在注册完成时还需要对邀请人进行奖励,记录邀请记录,对该 ...

  3. 利用自定义View实现扫雷游戏

    游戏规则: 简单版的扫雷事实上就是一个9×9的矩阵,其中有十个点是雷,非雷方块的数字代表该方块周围八个方块中雷的个数.通过长按某一方块(方块会变红)认定该方块为玩家认为的雷,通过短按某一方块来“展开” ...

  4. Android为TV端助力 MediaPlayer 错误代码(error code)总结 转载

    public static final int MEDIA_ERROR_IO Added in API level 17 File or network related operation error ...

  5. $符号报not defing 报错

    https://blog.csdn.net/weixin_37969488/article/details/84250833 最近因为工作问题,需要我把别的项目上的一些jsp网页copy到新项目上.放 ...

  6. java新知识系列 二

      1:数据库事务隔离以及事务隔离的级别 数据库事务隔离: 在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别:为了解决更新丢失,脏读,不可重读(包括虚读和幻读)等问题在标准SQL规 ...

  7. Node的简介

    从开始学习node到现在已经有半年多了,中间没有做过什么实际工作中的项目,所以感觉自己的知识有些匮乏,但是我还是要写这些文章,因为工作中的需要用node来开发后台环境,再加上我对这些知识记得不多,都是 ...

  8. 一个「学渣」从零开始的Web前端自学之路

    从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...

  9. MongoDB副本集功能及节点属性梳理

    副本集的主要功能 副本集是MongoDB高可用的基础,其主要作用 归纳为以下几点: (1)高可用,防止设备(服务器.网络)故障.提供自动FailOver功能. (2)无需配置高可用性虚拟节点:无论是S ...

  10. SQLServer之修改索引

    使用SSMS数据库管理工具修改索引 使用表设计器修改索引 表设计器可以修改任何类型的索引,修改索引的步骤相同,本示例为修改唯一非聚集索引. 1.连接数据库,选择数据库,选择数据表->右键点击表- ...