题面

传送门

题解

代码不就百来行么也不算很长丫

虽然这题随机化贪心就可以过而且速度和正解差不多不过我们还是要好好学正解

前置芝士

边分治

米娜应该都知道点分治是个什么东西,而边分治,顾名思义就是对边进行分治,即每次选出一条“子树中点的个数的最大值最小”的边,处理所有经过这条边的路径的贡献,然后割掉这条边之后对子树递归下去就好了

然而出题人给你一个菊花图就能把你卡得不要不要的

我们发现上述策略在一个二叉树上是最优的,因为割掉边之后左右子树大小都会变为原来的一半

于是这里就需要多叉树转二叉树,具体的流程可以看\(shadowice\)巨巨的图

这样的话点数最多变成\(2n\),但是分治的时候复杂度就有保证了

虚树

咱对这东西也不熟所以请米娜自己去找吧

本题

本题要求我们最大化

\[dis1(x,y)+dis2(x,y)+dis3(x,y)
\]

我们在第一棵树上边分,处理所有经过\((u,v)\)这条边的路径的贡献,那么肯定满足\(x\)和\(u\)在同一联通块(以下称为黑点),\(y\)和\(v\)在同一个联通块(以下称为白点),并且因为\((u,v)\)的长度是一个定值,那么我们需要最大化

\[dis1(x,u)+dis1(v,y)+dis2(x,y)+dis3(x,y)
\]

把它改写一下

\[dis1(x,u)+dis1(v,y)+dep2(x)+dep2(y)+dep3(x)+dep3(y)
\]

上面的柿子里是只和\(x,y\)有关的,那么我们还需要减去

\[2\times dep2(LCA2(x,y))+2\times dep3(LCA3(x,y))
\]

因为我们已经选定了\(u,v\),所以我们可以快速预处理出\(w(x)=dis1(x,u/v)+dep2(x)+dep3(x)\),那么就变成最大化

\[w(x)+w(y)-2\times dep2(LCA2(x,y))-2\times dep3(LCA3(x,y))
\]

发现每一次分治的时候满足条件的\(x,y\)都是一个点集,那么我们可以把这些点集放到第二颗树上做一个虚树

在虚树上,我们可以枚举\(LCA\),设为\(p\),那么此时\(dep2(LCA2(x,y))\)就是个定值了,我们需要求出两个点\(x,y\),其中\(x\)是黑点,\(y\)是白点,且\(x,y\)不在\(p\)的同一子树内,并且最大化

\[w(x)+w(y)-2\times dep3(LCA3(x,y))
\]

首先你需要知道一个结论:如果有一棵树\(T\),边权非负,直径的端点为\(u,v\),和一棵树\(G\),直径的端点为\(a,b\),那么用一条边将它们连接起来之后(无论把两棵树的那两个节点相连),直径一定为\(uv,ua,ub,va,vb,ab\)六条路径中的一条(这里的直径定义为不存在路径比它长,不过有可能有路径和它长度相等)

证明的话可以感性理解一下

然后这里就需要用到一个推论:对于两个树上的点集\(A,B\),\(A\)的直径端点为\(u,v\),\(B\)的直径端点为\(a,b\),那么一条左右端点分别在\(A,B\)中的最长的路径必为\(ua,uv,va,vb\)四条路径中的一条

然而放到这里有什么用么?\(w(x)+w(y)-2\times dep3(LCA3(x,y))\)怎么看也不像是树上两点的距离啊?

那么我们把它化一下,变成

\[dis1(x,u)+dep2(x)+dis2(y,v)+dep2(y)+dis3(x,y)
\]

那么我们可以看做是在第三棵树上,在\(x\)下面挂一个虚点\(x'\),\((x,x')\)的长度为\(dis1(x,u)+dep2(x)\),\(y\)同理,这样能看做是树上的一条路径了,也就是说我们关于树上路径的那个结论成立

然后就没有然后了,码吧

似乎有大佬写了随机化贪心的做法,这里说一下好了,就是每一次枚举根节点算出三棵树上其他点到它距离总和的最大值,用距离最大的点来更新根节点,然后每隔几次之后重新\(rand\)根节点……我调了调之后速度已经和我的正解差不多了(虽然会被\(uoj\)上的\(hack\)数据卡掉不过比赛的时候可没有这么强的数据啊)

虽然我估计考场上我可能这种随机化贪心都打不出来

可以对比一下下面两份代码的码量……

这份是随机化贪心,洛谷上能过,\(uoj\)会被\(hack\)

// luogu-judger-enable-o2
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define CL 1000.0*clock()/CLOCKS_PER_SEC
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
R ll res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=1e5+5;
struct eg{int v,nx;ll w;};
struct Gr{
eg e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
ll dis[N];
void dfs(int u,int fa){go(u)v!=fa?(dis[v]=dis[u]+e[i].w,dfs(v,u),0):0;}
}g[3];
ll res,w,las;bool vis[N];int n,u,v,c;
inline int rd(){return 1ll*rand()*rand()%n+1;}
void qwq(){
fp(rt,1,n){
g[0].dis[rt]=g[1].dis[rt]=g[2].dis[rt]=0;
g[0].dfs(rt,0),g[1].dfs(rt,0),g[2].dfs(rt,0);
fp(j,1,n)cmax(res,g[0].dis[j]+g[1].dis[j]+g[2].dis[j]);
}
printf("%lld\n",res);
}
int main(){
srand(19260817);
// freopen("testdata.in","r",stdin);
double st=CL;
n=read();
fp(i,1,n-1)u=read(),v=read(),w=read(),g[0].add(u,v,w),g[0].add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),g[1].add(u,v,w),g[1].add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),g[2].add(u,v,w),g[2].add(v,u,w);
if(n<=3500)return qwq(),0;
while(CL-st<=3400){
int rt=rd();las=res;
while(vis[rt])rt=rd();
fp(i,1,5){
if(vis[rt])break;
vis[rt]=1,g[0].dis[rt]=g[1].dis[rt]=g[2].dis[rt]=0;
g[0].dfs(rt,0),g[1].dfs(rt,0),g[2].dfs(rt,0);
ll mx=0;
fp(j,1,n)
cmax(res,g[0].dis[j]+g[1].dis[j]+g[2].dis[j]),
!vis[j]&&cmax(mx,g[0].dis[j]+g[1].dis[j]+g[2].dis[j])?rt=j:0;
}
las==res?++c:c=0;
if(c>=20)break;
}
printf("%lld\n",res);
return 0;
}

然后下面这份是正解

虽然它们都说求\(LCA\)要用\(ST\)表然而亲测用树剖似乎速度差不多?

// luogu-judger-enable-o2
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
R ll res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=5e5+5;
int lg[N],s[2][N],top[2],ty[N];ll w[N],res;
int n;
namespace tree3{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int top[N],dep[N],fa[N],sz[N],son[N];ll dis[N];
void dfs1(int u){
sz[u]=1,dep[u]=dep[fa[u]]+1;
go(u)if(v!=fa[u]){
dis[v]=dis[u]+e[i].w,fa[v]=u,dfs1(v),sz[u]+=sz[v];
sz[v]>sz[son[u]]?son[u]=v:0;
}
}
void dfs2(int u,int t){
top[u]=t;if(!son[u])return;
dfs2(son[u],t);
go(u)!top[v]?(dfs2(v,v),0):0;
}
inline int LCA(R int u,R int v){
while(top[u]!=top[v]){
dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
inline ll Dis(R int u,R int v){return w[u]+w[v]+dis[u]+dis[v]-(dis[LCA(u,v)]<<1);}
}
namespace tree2{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int top[N],dep[N],fa[N],sz[N],son[N],low[N],dfn[N],cnt;ll dis[N];
void dfs1(int u){
sz[u]=1,dep[u]=dep[fa[u]]+1,dfn[u]=++cnt;
go(u)if(v!=fa[u]){
dis[v]=dis[u]+e[i].w,fa[v]=u,dfs1(v),sz[u]+=sz[v];
sz[v]>sz[son[u]]?son[u]=v:0;
}
low[u]=cnt;
}
void dfs2(int u,int t){
top[u]=t;if(!son[u])return;
dfs2(son[u],t);
go(u)!top[v]?(dfs2(v,v),0):0;
}
inline int LCA(R int u,R int v){
while(top[u]!=top[v]){
dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
inline void Pre(){fp(i,1,n)head[i]=0;tot=0;}
struct node{
int u,v;ll d;
node():u(0),v(0),d(0){}
node(R int uu,R int vv,R ll dd):u(uu),v(vv),d(dd){}
node(R int uu,R int vv):u(uu),v(vv){d=tree3::Dis(u,v);}
inline bool operator <(const node &b)const{return !u?1:!b.u?0:d<b.d;}
inline node operator +(const node &b)const{
node res=max(*this,b);
cmax(res,max(node(u,b.u),node(u,b.v))),
cmax(res,max(node(v,b.u),node(v,b.v)));
return res;
}
inline ll merge(const node &b){
return max(max(node(u,b.u),node(u,b.v)),max(node(v,b.u),node(v,b.v))).d;
}
}f[N][2];
int q[N<<2],stack[N<<1],t;bool vis[N],ins[N];ll tag;
inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
void dp(int u){
f[u][0]=f[u][1]=node();
vis[u]?(f[u][ty[u]]=node(u,u,0),0):0;
go(u){
dp(v),
cmax(res,max(f[u][0].merge(f[v][1]),f[u][1].merge(f[v][0]))+tag-(dis[u]<<1)),
f[u][0]=f[u][0]+f[v][0],
f[u][1]=f[u][1]+f[v][1];
}
}
void solve(ll val){
tag=val,t=0,tot=0;
fp(i,1,::top[0])q[++t]=s[0][i];
fp(i,1,::top[1])q[++t]=s[1][i];
sort(q+1,q+1+t,cmp);
fp(i,1,t)vis[q[i]]=ins[q[i]]=1,w[q[i]]+=dis[q[i]];
for(R int i=t,lca;i>1;--i)lca=LCA(q[i],q[i-1]),!ins[lca]?(q[++t]=lca,ins[lca]=1):0;
sort(q+1,q+1+t,cmp);
for(R 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]);
fp(i,1,t)vis[q[i]]?w[q[i]]-=dis[q[i]]:0,vis[q[i]]=ins[q[i]]=head[q[i]]=0;
}
}
namespace tree1{
struct eg{int v,nx;ll w;}e[N<<2];int head[N<<1],tot=1;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
int cnt;
namespace qwq{
struct eg{int v,nx;ll w;}e[N<<1];int head[N],las[N],tot;
inline void add(R int u,R int v,R ll w){e[++tot]={v,head[u],w},head[u]=tot;}
void ins(int u,int v,ll w){
++cnt,tree1::add(cnt,v,w),tree1::add(v,cnt,w),
tree1::add(las[u],cnt,0),tree1::add(cnt,las[u],0),
las[u]=cnt;
}
void build(int u,int fa){go(u)if(v!=fa)ins(u,v,e[i].w),build(v,u);}
inline void init(){fp(i,1,n)las[i]=i;cnt=n;build(1,0);}
}
bool vis[N];int sz[N],rt,mx;ll dis[N];
void findrt(int u,int fa,int size){
sz[u]=1;
go(u)if(!vis[i>>1]&&v!=fa){
findrt(v,u,size),sz[u]+=sz[v];
cmin(mx,max(sz[v],size-sz[v]))?rt=i:0;
}
}
void dfs(int u,int fa,int op){
u<=n?ty[u]=op,s[op][++top[op]]=u:0;
go(u)if(v!=fa&&!vis[i>>1])
dis[v]=dis[u]+e[i].w,dfs(v,u,op);
}
void solve(int u,int size){
mx=1e9,findrt(u,0,size);
if(mx==1e9)return;
vis[rt>>1]=1;
int nw=rt,ss=size-sz[e[rt].v];
top[0]=top[1]=dis[e[rt].v]=dis[e[rt^1].v]=0;
dfs(e[rt].v,0,1),dfs(e[rt^1].v,0,0);
fp(i,1,top[0])w[s[0][i]]+=dis[s[0][i]];
fp(i,1,top[1])w[s[1][i]]+=dis[s[1][i]];
tree2::solve(e[rt].w);
fp(i,1,top[0])w[s[0][i]]-=dis[s[0][i]];
fp(i,1,top[1])w[s[1][i]]-=dis[s[1][i]];
solve(e[nw].v,sz[e[nw].v]),solve(e[nw^1].v,ss);
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=read();
fp(i,2,n+n)lg[i]=lg[i>>1]+1;
int u,v;ll w;
fp(i,1,n-1)u=read(),v=read(),w=read(),tree1::qwq::add(u,v,w),tree1::qwq::add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),tree2::add(u,v,w),tree2::add(v,u,w);
fp(i,1,n-1)u=read(),v=read(),w=read(),tree3::add(u,v,w),tree3::add(v,u,w);
tree2::dfs1(1),tree2::dfs2(1,1),tree2::Pre(),
tree3::dfs1(1),tree3::dfs2(1,1);
tree1::qwq::init(),tree1::solve(1,tree1::cnt);
printf("%lld\n",res);
return 0;
}

洛谷P4220 [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. 洛谷4103 HEOI2014大工程(虚树+dp)

    又是一道虚树好题啊 我们建出来虚树,然后考虑dp过程,我们分别令\(sum[x],mndis[x],mxdis[x],size[x]\)为子树内的路径长度和,最短链,最长链,子树内关键点个数. 对于一 ...

  5. 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)

    题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...

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

  7. 洛谷 P3377 【模板】左偏树(可并堆)

    洛谷 P3377 [模板]左偏树(可并堆) 题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或 ...

  8. 洛谷P3377 【模板】左偏树(可并堆) 题解

    作者:zifeiy 标签:左偏树 这篇随笔需要你在之前掌握 堆 和 二叉树 的相关知识点. 堆支持在 \(O(\log n)\) 的时间内进行插入元素.查询最值和删除最值的操作.在这里,如果最值是最小 ...

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

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

随机推荐

  1. 编写DLL

    想想还是把这个记录下吧,虽然不难,但由于平时写得不多,老是搞忘了. 1.我们来编写一个简单的DLL程序. 首先,我们来看下入口函数DllMain().DllMain()有3个参数: (1)hModul ...

  2. unity在安卓中横屏闪退

    竖屏没问题,横屏闪退 配置文件的AndoridManifest.xml横竖屏设置要和UNITY设置的一致,否则就会强退 UNITY横竖屏设置

  3. Oracle VM VirtualBox 部署CS devcloud2 开发环境

    Setting up (VirtualBox) 1. Get the new DevCloud 2.0 virtual appliance. The new image was created usi ...

  4. Qt Signal and Slot

    Qt4中的信号槽 Qt4中的信号槽是通过SIGNAL,SLOT两个宏,将参数转换成字符串.Qt编译前,会从源码的头文件中提取由signal和slot声明的信号和槽的函数, 将其组成一张信号和槽对应的字 ...

  5. centos 6.5使用virtualenv指定python 2.7.x

    1. 使用现有的 python 2.6.x 安装pip rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8. ...

  6. Tomcat内存溢出及大小调整

    一.在使用Java程序从数据库中查询大量的数据或是应用服务器(如tomcat.jboss,weblogic)加载jar包时会出现java.lang.OutOfMemoryError异常.这主要是由于应 ...

  7. python 数据清洗

    前言 1. 删除重复 2. 异常值监测 3. 替换 4. 数据映射 5. 数值变量类型化 6. 创建哑变量 统计师的Python日记[第7天:数据清洗(1)] 前言 根据我的Python学习计划: N ...

  8. MapServer:地图发布工具

    MapServer简介:https://baike.baidu.com/item/MapServer

  9. OpenCV4Android安装

    转:http://blog.csdn.net/gao_chun/article/details/49359535 1.下载及目录介绍 2.将 OpenCV引入 Android Studio 3.更新 ...

  10. 19、Semantic-UI之图片的动画效果

      在Semantic-UI中定义了很多图片动画效果,可以直接使用. 示例:定义图片动画 <!DOCTYPE html> <html lang="en"> ...