题意:给三棵基于同一点集的带边权的树,边权非负,求两点间三棵树上距离之和的最大值。

n≤105n\leq 10^5n≤105

一句话题解:在第一棵树上做边分治,丢到第二棵树上建虚树,在虚树上根据第三棵树的直径dp。

首先,这个问题难搞的地方只在于需要统计 lca。所以我们做的一切工作都是为了搞掉 lca 。

在求最大值的问题上有以下搞法:

  1. 树分治
  2. 枚举 lca 统计贡献。或者说就是 dp。

把这两个合起来就可以做 暴力写挂 了。但这个问题还多了一棵树,还要再用一个方法。

哲学分析,如果没有特殊性质,剩下这个方法肯定不会太弱于点分治,然后你就可以弃疗了。仔细观察,这题唯一有的特殊条件就只有藏在数据范围里的边权非负了。

边权非负的时候直径是可以合并的,所以第三个方法就是利用直径。

整理一下,在第一棵树上点分治,通过到分治中心的距离之和代替距离,搞掉第一棵树的 lca。在第二棵树的虚树上 dp,只在 lca 处更新答案,搞掉第二棵树的 lca。在 dp 时记录虚树的子树中两种颜色的点集在第三棵树上的直径,枚举端点合并,直接处理第三棵树上的距离。

设分治中心边的边权为 www,第一棵树上的点到分治中心的距离为 disdisdis,第二棵树上的点的深度为 depdepdep,第三棵树上两点距离为 distdistdist,我们相当于求这个东西的最大值

w+disa+disb+depa+depb−2deplca⁡(a,b)+dist(a,b)w+dis_a+dis_b+dep_a+dep_b-2dep_{\operatorname{lca}(a,b)}+dist(a,b)w+disa​+disb​+depa​+depb​−2deplca(a,b)​+dist(a,b)

我们假装在第三棵树上每个点挂两个叶子,边权为 disa+depadis_a+dep_adisa​+depa​,所以直径可合并的结论在端点有权值时也是成立的。

复杂度是 O(nlog⁡n)O(n\log n)O(nlogn)。

人生最长代码,不过很多复制粘贴,不算难写。

要注意虚树上的虚点不属于任何颜色。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <algorithm>
#define MAXN 200005
#define MAXM 400005
using namespace std;
typedef long long ll;
const int INF=0x7fffffff;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
inline ll readll()
{ll ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
struct edge{int u,v;ll w;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt=1;
inline void addnode(int u,int v,ll w)
{e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;
}
vector<edge> E[MAXN];
int vis[MAXN],n,tot;
void dfs(int u)
{vis[u]=1;if ((int)E[u].size()<=3){for (int i=0;i<(int)E[u].size();i++){int v=E[u][i].v;ll w=E[u][i].w;if (!vis[v])dfs(v),addnode(u,v,w),addnode(v,u,w);           }return;}int cur[2]={++tot,++tot},pos=0;addnode(u,cur[0],0),addnode(cur[0],u,0);addnode(u,cur[1],0),addnode(cur[1],u,0);for (int i=0;i<(int)E[u].size();i++)if (!vis[E[u][i].v])E[cur[pos^=1]].push_back(E[u][i]);dfs(cur[0]),dfs(cur[1]);
}
int rt,mi,siz[MAXN];
void findrt(int u,int f,int sum)
{siz[u]=1;for (int i=head[u];i;i=nxt[i])if (!vis[i>>1]&&e[i].v!=f){findrt(e[i].v,u,sum);if (max(siz[e[i].v],sum-siz[e[i].v])<mi) mi=max(siz[e[i].v],sum-siz[e[i].v]),rt=i;siz[u]+=siz[e[i].v];}
}
int LOG[MAXM];
namespace FT
{edge e[MAXM];int head[MAXN],nxt[MAXM],cnt;inline void addnode(int u,int v,ll w){e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;}ll dis[MAXN],val[MAXN];int dfn[MAXN],lis[MAXM],tim;inline bool cmp(const int& x,const int& y){return dfn[x]<dfn[y];}void dfs(int u,int f){lis[dfn[u]=++tim]=u;for (int i=head[u];i;i=nxt[i])if (e[i].v!=f){dis[e[i].v]=dis[u]+e[i].w;dfs(e[i].v,u);lis[++tim]=u;}}int st[MAXM][20];inline int lca(int x,int y){x=dfn[x],y=dfn[y];if (x>y) swap(x,y);int t=LOG[y-x+1];return min(st[x][t],st[y-(1<<t)+1][t],cmp);}inline ll dist(int x,int y){return x&&y? dis[x]+dis[y]+val[x]+val[y]-2*dis[lca(x,y)]:-1;}void input(){for (int i=1;i<n;i++){int u,v;ll w;u=read(),v=read(),w=readll();addnode(u,v,w),addnode(v,u,w);}dfs(1,0);for (int i=1;i<=tim;i++) st[i][0]=lis[i];for (int j=1;j<20;j++)for (int i=1;i+(1<<(j-1))<=tim;i++)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1],cmp);}
}
namespace VT
{edge e[MAXM];int head[MAXN],nxt[MAXM],cnt;inline void addnode(int u,int v,ll w){e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt;}ll dis[MAXN];int dfn[MAXN],lis[MAXM],tim;inline bool cmp(const int& x,const int& y){return dfn[x]<dfn[y];}void dfs(int u,int f){lis[dfn[u]=++tim]=u;for (int i=head[u];i;i=nxt[i])if (e[i].v!=f){dis[e[i].v]=dis[u]+e[i].w;dfs(e[i].v,u);lis[++tim]=u;}}int st[MAXM][20],col[MAXN];inline int lca(int x,int y){x=dfn[x],y=dfn[y];if (x>y) swap(x,y);int t=LOG[y-x+1];return min(st[x][t],st[y-(1<<t)+1][t],cmp);}void input(){memset(col,-1,sizeof(col));for (int i=1;i<n;i++){int u,v;ll w;u=read(),v=read(),w=readll();addnode(u,v,w),addnode(v,u,w);}dfs(1,0);for (int i=1;i<=tim;i++) st[i][0]=lis[i];for (int j=1;j<20;j++)for (int i=1;i+(1<<(j-1))<=tim;i++)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1],cmp);}vector<int> p,son[MAXN];struct path{int x,y;inline path(const int& x=0,const int& y=0):x(x),y(y){}};inline ll calc(const path& a){return FT::dist(a.x,a.y);}inline bool operator <(const path& a,const path& b){return calc(a)<calc(b);}int stk[MAXN],tp;path mx[MAXN][2];ll ans;void dfs(int u){mx[u][0]=mx[u][1]=path(0,0);if (~col[u]) mx[u][col[u]]=path(u,u);for (int i=0;i<(int)son[u].size();i++){int v=son[u][i];dfs(v);path t;t=max(t,path(mx[u][0].x,mx[v][1].x));t=max(t,path(mx[u][0].x,mx[v][1].y));t=max(t,path(mx[u][0].y,mx[v][1].x));t=max(t,path(mx[u][0].y,mx[v][1].y));t=max(t,path(mx[u][1].x,mx[v][0].x));t=max(t,path(mx[u][1].x,mx[v][0].y));t=max(t,path(mx[u][1].y,mx[v][0].x));t=max(t,path(mx[u][1].y,mx[v][0].y));ans=max(ans,calc(t)-2*dis[u]);t=max(mx[u][0],mx[v][0]);t=max(t,path(mx[u][0].x,mx[v][0].x));t=max(t,path(mx[u][0].x,mx[v][0].y));t=max(t,path(mx[u][0].y,mx[v][0].x));t=max(t,path(mx[u][0].y,mx[v][0].y));mx[u][0]=t;t=max(mx[u][1],mx[v][1]);t=max(t,path(mx[u][1].x,mx[v][1].x));t=max(t,path(mx[u][1].x,mx[v][1].y));t=max(t,path(mx[u][1].y,mx[v][1].x));t=max(t,path(mx[u][1].y,mx[v][1].y));mx[u][1]=t;}}ll solve(){sort(p.begin(),p.end(),cmp);int s=p.size();for (int i=0;i<s-1;i++) p.push_back(lca(p[i],p[i+1]));sort(p.begin(),p.end(),cmp);p.erase(unique(p.begin(),p.end()),p.end());tp=0;for (int i=0;i<(int)p.size();i++){while (tp&&lca(stk[tp],p[i])!=stk[tp]) --tp;if (tp) son[stk[tp]].push_back(p[i]);stk[++tp]=p[i];}ans=0;dfs(stk[1]);for (int i=0;i<(int)p.size();i++) son[p[i]].clear(),col[p[i]]=-1;p.clear();return ans;}
}
void dfs(int u,int f,int c,ll d)
{if (u<=n) FT::val[u]=d+VT::dis[u],VT::p.push_back(u),VT::col[u]=c;for (int i=head[u];i;i=nxt[i])if (!vis[i>>1]&&e[i].v!=f)dfs(e[i].v,u,c,d+e[i].w);
}
ll ans;
void solve(int sum)
{if (mi==INF) return;vis[rt>>1]=1;dfs(e[rt].v,0,0,0);dfs(e[rt].u,0,1,0); ans=max(ans,VT::solve()+e[rt].w);int cur=rt,sz=siz[e[rt].v];mi=INF,findrt(e[cur].v,0,sz),solve(sz);mi=INF,findrt(e[cur].u,0,sum-sz),solve(sum-sz);
}
int main()
{LOG[0]=-1;for (int i=1;i<MAXM;i++) LOG[i]=LOG[i>>1]+1;tot=n=read();for (int i=1;i<n;i++){int u,v;ll w;u=read(),v=read(),w=readll();E[u].push_back((edge){u,v,w}),E[v].push_back((edge){v,u,w});}dfs(1);memset(vis,0,sizeof(vis));VT::input();FT::input();mi=INF,findrt(1,0,tot),solve(tot);cout<<ans;return 0;
}

【WC2018】通道【边分治】【虚树】【树的直径】相关推荐

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

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

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

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

  3. 虚树+树型DP SDOI2011消耗战

    <虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring&g ...

  4. 【2016北京集训测试赛(八)】 直径 (虚树+树的直径)

    Description 注意:时限更改为4s 题解 考虑最原始的直径求法:找到离根节点(或离其他任意一点)最远的节点far1,再从far1出发找到离far1最远的节点far2,far1至far2的距离 ...

  5. 2021牛客多校6K Starch Cat(点分治+猫树)

    大意就是给一个树,给很多个问询,选择两个点之间的若干个点,在保证选择一个点后与其直接相连的点不能选,每次询问求总和最大值.必须在线. 最开始想的是树剖,但是做完签到后一看K题只有几个AC就果断run了 ...

  6. 主题图片_临床医学院“树树皆秋色,山山唯落晖”主题图片征集活动

    自古逢秋悲寂寥,我言秋日胜春朝.秋风起,金叶舞,趵突泉校区迎来了金色的秋天.在老舍先生笔下,冬天是济南最美的季节,而在同学们的眼中,趵突泉校区的秋色更是别具一番风味.临床医学院新媒体中心特此举办&qu ...

  7. The Trip On Abandoned Railway(线段树+树状数组)

    链接:https://ac.nowcoder.com/acm/problem/13891 来源:牛客网 题目描述 There are many ghosts at the abandoned stat ...

  8. [XSY] 智慧树(线性同余方程组,线段树/树状数组)

    智慧树 解决此题有两个要点: 如何判断一个线性同余方程组有没有解 如何统计合法子序列数目 先看第2点: 若一个序列是合法的,则这个序列的所有子序列都是合法的 考虑对∀1≤i≤n\forall 1\le ...

  9. [Bzoj2243][SDOI2011]染色(线段树树剖)

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2243 线段树+树链剖分,在线段树需要每次用lt和rt两个数组记录当前区间的左右边界的颜色 ...

  10. 408数据结构学习笔记-树-①树的逻辑结构

    目录 ①定义 ②结点间的关系描述 ③结点路径 ④结点,树的属性描述 ⑤有序树和无序树 ⑥森林 ⑦树的性质(6个性质) a.结点数=总度数+1 b.度为m的树和m叉树的区别 c.度为m的树第i层至多有 ...

最新文章

  1. FSD键盘钩子框架参考爱写驱动的女装大佬
  2. java new thread参数_java线程池01-ThreadPoolExecutor构造方法参数的使用规则
  3. 2012-04-26 16:08 Director installer fails with error Unable to run run-cds-tool script“
  4. OData metadata 定义中,entity type key 的作用是什么
  5. SOL注入——HTTP头部注入(六)
  6. [转载]Informix Dynamic Server维护手册
  7. Atmel megaAVR控制器 串行引导Bootloader
  8. 树莓派基金会来号召用键盘生物学家研究企鹅
  9. 数据结构|-二叉查找树(二叉搜索树)的链式存储结构的实现
  10. windows下使用vim
  11. CurrentUser获取不到当前登录用户name
  12. 华为交换机开启ftp服务,上传和下载文件,get和put操作实例
  13. vivado2021.2安装及验证教程(绝大部分适用于同软件其他版本)
  14. Axure手机原型图总结
  15. 第十届山东理工大学ACM网络编程擂台赛 重现
  16. 运算放大器---封装尺寸
  17. IBM推出企业信息安全框架
  18. 聚类算法K-Means
  19. java 从set取值_怎样从java集合类set中取出数据?
  20. Speedoffice(Word)怎样设置页眉页脚高度

热门文章

  1. 这个偏僻的小山村竟出了12位博士28位硕士,高产“学霸”背后原因曝光......
  2. 看OpenCV如何在python中实现图像检测!
  3. 原来R语言还有这些不为人知的用处!
  4. 分析函数在数据分析中的应用
  5. java launcher 下载_Java Launcher下载并安装
  6. java 线程 插件_我的第一个Chrome插件:天气预报应用
  7. mysql innodb表损坏_MySQL数据库INNODB表损坏修复处理过程分享
  8. 世道变了,面试初级Java开发会问到Arrays!!!你不会还不知道吧!
  9. 超详细图解!【MySQL进阶篇】存储过程,视图,索引,函数,触发器
  10. php return 变量,php内核笔记–函数返回变量return_value