【WC2018】通道(边分治,虚树,动态规划)

题面

UOJ

洛谷

题解

既然是三棵树,那么显然就是找点什么东西来套个三层。

一棵树怎么做?入门dp。

两棵树?假设在第一棵树中的深度为\(dep\)。在第一棵树中枚举\(LCA\),因为两点之间距离可以转化为两点深度和减去两倍\(LCA\)的深度,而已知当前的\(LCA\)是谁了。那么在第二棵树上的每个节点\(u\)下挂一个\(u'\),边权为\(dep[u]\),那么枚举\(LCA\)之后答案就是在当前\(LCA\)的不同子树中的两点\((x\)在第二棵树上的\((x',y')\)的距离最大值。而动态维护两点距离最大值,也就是动态维护直径,直径可以很简单的合并。那么只需要维护一下枚举到每个\(LCA\)的时候在第二棵树上的直径就好了。大概是线性的?

然后三棵树?

那么第一层就边分治解决。

考虑过边\((u,v)\)的贡献,假设在两侧子树中选择的点是\((x,y)\)。

那么答案就是:\(dis1(x,y)+dis2(x,y)+dis3(x,y)\)。

此时\(dis1(x,y)\)已知,只需要考虑剩下的两个部分。

因为这个计算同时涉及到两个点,因此我们不得不枚举点对。

但是我们知道:\(dis(x,y)=dis(root,x)+dis(root,y)-2dis(root,LCA(x,y))\),假设到根的距离为\(dep\),就是\(dis(x,y)=dep(x)+dep(y)-2dep(LCA)\)。

显然通过边分治我们可以知道单独与某个点相关的所有贡献,因此唯一需要考虑的就是剩下的\(2dep2(LCA2(x,y))+2dep3(LCA3(x,y))\)。

定义\(w(x)=D(x)+dep2(x)+dep3(x)\),其中\(D\)表示到达分治边的距离。

那么现在要做的就是最大化\(w(x)+w(y)-2dep2(LCA2(x,y))-2dep3(LCA3(x,y))\)。

现在\(w(x),w(y)\)在边分治之后已知,考虑计算后面两个东西。

我们把当前分治所考虑的所有点给丢到\(T2\)上面构建虚树,这样子可以在\(LCA2\)处合并答案,那么我们在虚树上考虑在哪里合并,此时就是要找到当前点的不同子树中的\((x,y)\),而\(dep(LCA2)\)因为在此合并答案,所以此时已知,所以要求的变成了最大化\(w(x)+w(y)-2dep3(LCA3)\)。

似乎又看见了枚举\(LCA\)。然后回想起了两棵树的做法,于是又在\(u\)底下挂一个\(u'\),权值为\(D(x)+dep2(x)\)。那么又变成了在虚树上枚举\(LCA\),然后求不同子树上的点在第三棵树上的\((u',v')\)的距离最大值了\(QwQ\)。

似乎说起来就这么多了。。。

写起来瞬间\(6k+\)了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 400100
inline ll read()
{
ll x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int lg[MAX];
int S[2][MAX],top[2],type[MAX];
ll W[MAX],ans;
namespace Tree3
{
struct Line{int v,next;ll w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int st[20][MAX],sum,fir[MAX];
int dep[MAX];ll dis[MAX];
void dfs(int u,int ff)
{
st[0][++sum]=u;fir[u]=sum;dep[u]=dep[ff]+1;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u),st[0][++sum]=u;
}
int Compare(int a,int b){return dep[a]<dep[b]?a:b;}
void pre()
{
for(int j=1;j<=lg[sum];++j)
for(int i=1;i+(1<<j)-1<=sum;++i)
st[j][i]=Compare(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
int LCA(int u,int v)
{
u=fir[u];v=fir[v];if(u>v)swap(u,v);
int l=lg[v-u+1];
return Compare(st[l][u],st[l][v-(1<<l)+1]);
}
ll Dis(int u,int v){return W[u]+W[v]+dis[u]+dis[v]-2*dis[LCA(u,v)];}
} namespace Tree2
{
struct Line{int v,next;ll w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int st[20][MAX],sum;
int dfn[MAX],low[MAX],tim,fir[MAX];
int dep[MAX];ll dis[MAX];
void dfs(int u,int ff)
{
st[0][++sum]=u;dfn[u]=++tim;fir[u]=sum;dep[u]=dep[ff]+1;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u),st[0][++sum]=u;
low[u]=tim;
}
int Compare(int a,int b){return dep[a]<dep[b]?a:b;}
void pre()
{
for(int j=1;j<=lg[sum];++j)
for(int i=1;i+(1<<j)-1<=sum;++i)
st[j][i]=Compare(st[j-1][i],st[j-1][i+(1<<(j-1))]);
memset(h,0,sizeof(h));cnt=1;
}
int LCA(int u,int v)
{
u=fir[u];v=fir[v];if(u>v)swap(u,v);
int l=lg[v-u+1];
return Compare(st[l][u],st[l][v-(1<<l)+1]);
}
struct Pair{int u,v;ll dis;}f[MAX][2];
Pair make(int u,int v,ll dis){if(u>v)swap(u,v);return (Pair){u,v,dis};}
Pair make(int u,int v){if(u>v)swap(u,v);return (Pair){u,v,Tree3::Dis(u,v)};}
bool operator<(Pair a,Pair b){if(!a.u)return true;if(!b.u)return false;return a.dis<b.dis;}
Pair operator+(Pair a,Pair b)
{
Pair ret=max(a,b);
ret=max(ret,max(make(a.u,b.u),make(a.u,b.v)));
ret=max(ret,max(make(a.v,b.u),make(a.v,b.v)));
return ret;
}
ll Calc(Pair a,Pair b){return max(max(make(a.v,b.u),make(a.v,b.v)),max(make(a.u,b.u),make(a.u,b.v))).dis;}
int Q[MAX<<2],T,Stack[MAX<<1];
bool cmp(int a,int b){return dfn[a]<dfn[b];}
bool vis[MAX];
void DP(int u,ll Pls)
{
f[u][0]=f[u][1]=make(0,0,0);
if(vis[u])f[u][type[u]]=make(u,u,0);
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;DP(v,Pls);
ans=max(ans,max(Calc(f[u][0],f[v][1]),Calc(f[u][1],f[v][0]))+Pls-2*dis[u]);
f[u][0]=f[u][0]+f[v][0];
f[u][1]=f[u][1]+f[v][1];
}
}
void Solve(ll Pls)
{
T=0;cnt=1;
for(int i=1;i<=top[0];++i)Q[++T]=S[0][i];
for(int i=1;i<=top[1];++i)Q[++T]=S[1][i];
sort(&Q[1],&Q[T+1],cmp);
for(int i=1;i<=T;++i)W[Q[i]]+=dis[Q[i]];
for(int i=1;i<=T;++i)vis[Q[i]]=true;
for(int i=T;i>1;--i)Q[++T]=LCA(Q[i],Q[i-1]);
sort(&Q[1],&Q[T+1],cmp);T=unique(&Q[1],&Q[T+1])-Q-1;
for(int i=1,top=0;i<=T;++i)
{
while(top&&low[Stack[top]]<dfn[Q[i]])--top;
if(top)Add(Stack[top],Q[i],dis[Q[i]]-dis[Stack[top]]);Stack[++top]=Q[i];
}
DP(Q[1],Pls);
for(int i=1;i<=T;++i)if(vis[Q[i]])W[Q[i]]-=dis[Q[i]];
for(int i=1;i<=T;++i)vis[Q[i]]=false,h[Q[i]]=0;
}
} namespace Tree1
{
struct Line{int v,next;ll w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,ll w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
vector<int> son[MAX];
int n,N;ll V[MAX];
void dfs(int u,int ff)
{
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)son[u].push_back(e[i].v),V[e[i].v]=e[i].w,dfs(e[i].v,u);
}
void ReBuild()
{
cnt=2;memset(h,0,sizeof(h));
for(int i=1;i<=N;++i)
{
int l=son[i].size();
if(l<=2)
for(int j=0;j<l;++j)
Add(i,son[i][j],V[son[i][j]]),Add(son[i][j],i,V[son[i][j]]);
else
{
int ls=++N,rs=++N;
Add(i,ls,0);Add(ls,i,0);Add(i,rs,0);Add(rs,i,0);
for(int j=0;j<l;++j)
if(j&1)son[ls].push_back(son[i][j]);
else son[rs].push_back(son[i][j]);
}
}
}
bool vis[MAX];
int size[MAX],mx,rt;
ll dis[MAX];
void getroot(int u,int ff,int Size)
{
size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(vis[i>>1]||v==ff)continue;
getroot(v,u,Size);size[u]+=size[v];
int ret=max(size[v],Size-size[v]);
if(ret<mx)mx=ret,rt=i;
}
}
void dfs(int u,int ff,int opt)
{
if(u<=n)type[u]=opt,S[opt][++top[opt]]=u;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff&&!vis[i>>1])
dis[e[i].v]=dis[u]+e[i].w,dfs(e[i].v,u,opt);
}
void Divide(int u,int Size)
{
mx=1e9;getroot(u,0,Size);
if(mx>=1e9)return;
vis[rt>>1]=true;int nw=rt,SS=Size-size[e[rt].v];
top[0]=top[1]=dis[e[nw].v]=dis[e[nw^1].v]=0;
dfs(e[nw].v,0,0);dfs(e[nw^1].v,0,1);
for(int i=1;i<=top[0];++i)W[S[0][i]]+=dis[S[0][i]];
for(int i=1;i<=top[1];++i)W[S[1][i]]+=dis[S[1][i]];
Tree2::Solve(e[rt].w);
for(int i=1;i<=top[0];++i)W[S[0][i]]-=dis[S[0][i]];
for(int i=1;i<=top[1];++i)W[S[1][i]]-=dis[S[1][i]];
Divide(e[nw].v,size[e[nw].v]);
Divide(e[nw^1].v,SS);
}
}
int n;
int main()
{
n=read();Tree1::N=Tree1::n=n;
for(int i=2;i<=n+n;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<n;++i)
{
int u=read(),v=read();ll w=read();
Tree1::Add(u,v,w);Tree1::Add(v,u,w);
}
for(int i=1;i<n;++i)
{
int u=read(),v=read();ll w=read();
Tree2::Add(u,v,w);Tree2::Add(v,u,w);
}
for(int i=1;i<n;++i)
{
int u=read(),v=read();ll w=read();
Tree3::Add(u,v,w);Tree3::Add(v,u,w);
}
Tree2::dfs(1,0);Tree2::pre();
Tree3::dfs(1,0);Tree3::pre();
Tree1::dfs(1,0);Tree1::ReBuild();Tree1::Divide(1,Tree1::N);
printf("%lld\n",ans);
return 0;
}

【WC2018】通道(边分治,虚树,动态规划)的更多相关文章

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

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

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

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

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

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

  4. 【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\)两点 ...

  5. UOJ347 WC2018 通道 边分治、虚树

    传送门 毒瘤数据结构题qwq 设三棵树分别为$T1,T2,T3$ 先将$T1$边分治,具体步骤如下: ①多叉树->二叉树,具体操作是对于每一个父亲,建立与儿子个数相同的虚点,将父亲与这些虚点穿成 ...

  6. 洛谷P4220 [WC2018]通道(边分治+虚树)

    题面 传送门 题解 代码不就百来行么也不算很长丫 虽然这题随机化贪心就可以过而且速度和正解差不多不过我们还是要好好学正解 前置芝士 边分治 米娜应该都知道点分治是个什么东西,而边分治,顾名思义就是对边 ...

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

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

  8. LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...

  9. 【XSY3350】svisor - 点分治+虚树dp

    题目来源:NOI2019模拟测试赛(九) 题意: 吐槽: 第一眼看到题觉得这不是震波的完全弱化版吗……然后开开心心的码了个点分治 码到一半突然发现看错题了……心态崩了于是就弃疗手玩提答去了 于是就快乐 ...

  10. 洛谷 P6199 - [EER1]河童重工(点分治+虚树)

    洛谷题面传送门 神仙题. 首先看到这样两棵树的题目,我们肯定会往动态树分治的方向考虑.考虑每次找出 \(T_2\) 的重心进行点分治.然后考虑跨过分治中心的点对之间的连边情况.由于连边边权与两棵树都有 ...

随机推荐

  1. Vue之子组件

    全局组件 <script src="./static/vue.min.js"></script> // 导入vue <body> <div ...

  2. MySQL 高可用性—keepalived+mysql双主

    MySQL 高可用性—keepalived+mysql双主(有详细步骤和全部配置项解释) - 我的博客 - CSDN博客https://blog.csdn.net/qq_36276335/articl ...

  3. 开发神器之phpstorm破解与日常使用

    PhpStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具,旨在提高用户效率,可深刻理解用户的编码,提供智能代码补全,快速导航以及即时错误检查. PhpStorm可随时帮助用 ...

  4. MySQL数据类型优化—整数类型优化选择

    原文:http://bbs.landingbj.com/t-0-240002-1.html 在设计数据库的时候,整数类型的使用时不可避免的如ID,类型等. 在选择整数的同时主要是考虑是数据范围,如是否 ...

  5. laravel打印sql

    DB::connection()->enableQueryLog(); print_r(DB::getQueryLog());

  6. 10 Comparisons with adjectvies and nouns

    1 比较级用来比较两个词条之间的关系,比较级是通过在形容词后加 er 或者在形容词之前加 more 构成. 它的反义句是通过在形容词前加 less 或者 not as构成. Perfume sales ...

  7. Azure系列2.1.13 —— CloudBlockBlob

    (小弟自学Azure,文中有不正确之处,请路过各位大神指正.) 网上azure的资料较少,尤其是API,全是英文的,中文资料更是少之又少.这次由于公司项目需要使用Azure,所以对Azure的一些学习 ...

  8. [转帖]SAP一句话入门:Finacial & Controlling Accounting

    SAP一句话入门:Finacial & Controlling Accounting http://blog.vsharing.com/MilesForce/A621147.html 财务,财 ...

  9. 安装sqlprompt

    特别说明:注册机会报毒,安装前请先关闭杀毒软件!下载好附件之后解压,打开SQLPrompt_7.2.0.241.exe按照提示安装完成.安装完成后断网!打开数据库,会在菜单栏中看到SQL Prompt ...

  10. 剑指offer(15)

    题目: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)). 书中的思路: 按照这个思路我们很容易写出以下代码: import java.util.S ...