题目描述

http://uoj.ac/problem/347

题解

解法1

求三棵树的直径,看起来非常不可做,但是所有边权都是正的,可以让我们想到爬山。

所以我们可以按照BFS求树的直径的方法,随机一个点作为起点,然后BFS一遍,找到在这三棵树的意义下最远的那个点,然后继续爬山。

因为这样做没啥正确性,所以再卡一下时间就好了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#define get_clock (double)clock()/(double)CLOCKS_PER_SEC*1000.0
#define N 100009
using namespace std;
typedef long long ll;
ll dis[3][N],n,ans;
bool vis[N];
inline ll rd(){ll x=0;char c=getchar();bool f=0;while(!isdigit(c)){if(c=='-')f=1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}return f?-x:x;
}
struct Edge{int head[N],tot;struct ljb{int n,to;ll l;}e[N<<1];inline void add(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=l;}
}edge[3];
void dfs(int u,int fa,int tag){for(int i=edge[tag].head[u];i;i=edge[tag].e[i].n)if(edge[tag].e[i].to!=fa){int v=edge[tag].e[i].to;dis[tag][v]=dis[tag][u]+edge[tag].e[i].l;dfs(v,u,tag);}
}
void BF(){for(int x=1;x<=n;++x){dis[0][x]=dis[1][x]=dis[2][x]=0;dfs(x,0,0);dfs(x,0,1);dfs(x,0,2);for(int i=1;i<=n;++i){ans=max(ans,dis[0][i]+dis[1][i]+dis[2][i]);} }
}
int main(){srand(19260817);double start=get_clock;n=rd();ll u,v,w;for(int i=1;i<n;++i){u=rd();v=rd();w=rd();edge[0].add(u,v,w);}for(int i=1;i<n;++i){u=rd();v=rd();w=rd();edge[1].add(u,v,w);}for(int i=1;i<n;++i){u=rd();v=rd();w=rd();edge[2].add(u,v,w);}if(n<=3000){BF();}else{while(get_clock-start<=3500){int x=rand()%n+1;while(vis[x])x=rand()%n+1;for(int j=1;j<=9;++j){if(vis[x])break;vis[x]=1;dis[0][x]=dis[1][x]=dis[2][x]=0;dfs(x,0,0);dfs(x,0,1);dfs(x,0,2);ll now=0;for(int i=1;i<=n;++i){ll num=dis[0][i]+dis[1][i]+dis[2][i];ans=max(ans,num);if(num>now){now=num;if(!vis[i])x=i;}} } }}cout<<ans;return 0;
}

这样做可以通过官方数据,但是在UOJ上会被HACK。

解法2

我们考虑对第一棵树边分治,设当前的重心边为i。

那么我们需要最大化的东西就是deep1[x]+deep1[y]+val[i]+dist2(x,y)+dist3(x,y)。

其中xy是当前边分的块内点,且分居在i的两侧,我们可以设两边的颜色为黑色和白色。

这样我们相当于给x和y加上了一个权,并且去掉了第一棵树的LCA的限制。

而且val的影响可以先不讨论了。

然后我们考虑对第二颗树对目前边分的点建立虚树。

令value[x]表示deep1[x]+deep2[x]

然后我们在dfs虚树的时候枚举LCA,那么此时我们要最大化的东西就是value[x]+value[y]-2*deep2[lca]+dist3(x,y),需满足x和y在LCA的不同子树中。

然后最后的是个常数,而不用管。

然后value[x]+value[y]+dist3(x,y)这个东西,有点像树的直径。

我们可以想象一下,在第三棵树上的每一个点i我们都往外连出一条出边连向i',边权为value[i],这样上面的东西就变成了树的直径问题。

有一个结论,有两个点集,每个点集的直径为x-y,那么跨越两个点集的直径的两个端点一定在那两个点集的端点中出现过。

这个结论在边权非负的条件下成立。

所以我们对黑白两个点集分别维护直径,在子树合并的时候更新答案就好了。

其实想到这里,这道题就不难了,就是码量有点大。

ZZ错误:求LCA倍增写错,合并直径时没有考虑只有一个点的情况。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define inf 1e18
#define N 100009
using namespace std;
typedef long long ll;
int dfn[N],s,n,NTT;
inline ll rd(){ll x=0;char c=getchar();bool f=0;while(!isdigit(c)){if(c=='-')f=1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}return f?-x:x;
}
struct node{int tag,x;ll y;inline bool operator <(const node &b)const{return dfn[x]<dfn[b.x];}
}a[N];
namespace T3{int head[N],tot,p[20][N<<1],tag[N],lo[N<<1];ll dis[N];struct edge{int n,to;ll l;}e[N<<1];inline void add(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;}void dfs(int u,int fa){p[0][++tag[0]]=u;tag[u]=tag[0];for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){int v=e[i].to;dis[v]=dis[u]+e[i].l;dfs(v,u);p[0][++tag[0]]=u;}}inline int great(int x,int y){return tag[x]>tag[y]?y:x;}inline int getlca(int u,int v){int x=tag[u],y=tag[v];if(x>y)swap(x,y);int llo=lo[y-x+1];return great(p[llo][x],p[llo][y-(1<<llo)+1]);}inline void prework(){dfs(1,0);for(int i=1;(1<<i)<=tag[0];++i)for(int j=1;j<=tag[0];++j)p[i][j]=great(p[i-1][j],p[i-1][j+(1<<i-1)]);///!!!!for(int i=2;i<=tag[0];++i)lo[i]=lo[i>>1]+1;}inline ll calc(int x,int y){return dis[x]+dis[y]-2*dis[getlca(x,y)];}
}
namespace T2{int head[N],tot,p[20][N<<1],tag[N],lo[N<<1],vis[N],st[N],top,rbs[N];ll dis[N],val[N],ans;vector<int>::iterator it;vector<int>vec[N];struct edge{int n,to;ll l;}e[N<<1];inline void add(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;}struct DP{int x[2];inline bool empty(int x,int y){return (!x&&!y);}inline void clear(){x[0]=x[1]=0;}}dp[N][3];void dfs(int u,int f){p[0][++tag[0]]=u;tag[u]=tag[0];dfn[u]=++dfn[0];for(int i=head[u];i;i=e[i].n)if(e[i].to!=f){int v=e[i].to;dis[v]=dis[u]+e[i].l;dfs(v,u);p[0][++tag[0]]=u;}} inline int great(int x,int y){return tag[x]>tag[y]?y:x;}inline int getlca(int u,int v){int x=tag[u],y=tag[v];if(x>y)swap(x,y);int llo=lo[y-x+1];return great(p[llo][x],p[llo][y-(1<<llo)+1]);}inline void prework(){dfs(1,0); for(int i=1;(1<<i)<=tag[0];++i)for(int j=1;j+(1<<i)-1<=tag[0];++j)p[i][j]=great(p[i-1][j],p[i-1][j+(1<<i-1)]);for(int i=2;i<=tag[0];++i)lo[i]=lo[i>>1]+1;}inline ll calc(int x,int y){if(!x||!y)return -inf;return val[x]+val[y]+T3::calc(x,y);}void ddp(int u){if(vis[u])dp[u][vis[u]].x[0]=u;for(int i=0;i<vec[u].size();++i){int v=vec[u][i];ddp(v);for(int i=1;i<=2;++i)for(int j=0;j<2;++j)for(int k=0;k<2;++k){int d=i==1?2:1,x=dp[u][i].x[j],y=dp[v][d].x[k];ans=max(ans,calc(x,y)-2ll*dis[u]);}for(int i=1;i<=2;++i){if(!dp[u][i].x[0]&&!dp[u][i].x[1]){dp[u][i].x[0]=dp[v][i].x[0];dp[u][i].x[1]=dp[v][i].x[1];continue;}if(!dp[v][i].x[0]&&!dp[v][i].x[1])continue;int maxx=dp[u][i].x[0],maxy=dp[u][i].x[1];ll you=calc(maxx,maxy),bian=calc(dp[v][i].x[0],dp[v][i].x[1]);if(bian>you)maxx=dp[v][i].x[0],maxy=dp[v][i].x[1],you=bian;for(int j=0;j<2;++j)for(int k=0;k<2;++k){int x=dp[u][i].x[j],y=dp[v][i].x[k];bian=calc(x,y);if(bian>you)you=bian,maxx=x,maxy=y;}dp[u][i].x[0]=maxx;dp[u][i].x[1]=maxy;}}}inline ll work(){ans=-inf;sort(a+1,a+s+1);for(int i=1;i<=s;++i){vis[a[i].x]=a[i].tag;val[a[i].x]=a[i].y+dis[a[i].x];}int start=1;st[top=1]=rbs[rbs[0]=1]=1;if(a[1].x==1)start++;for(int i=start;i<=s;++i){int x=a[i].x,lc=getlca(x,st[top]);if(lc==st[top]){st[++top]=x;rbs[++rbs[0]]=x;continue;}while(top>1){int xx=st[top],yy=st[top-1];if(dfn[yy]<=dfn[lc]){vec[lc].push_back(xx);top--;break;}vec[yy].push_back(xx);top--;} if(st[top]!=lc){st[++top]=lc;rbs[++rbs[0]]=lc;}if(st[top]!=x){st[++top]=x;rbs[++rbs[0]]=x;}}while(top>1){vec[st[top-1]].push_back(st[top]);top--;}ddp(1);while(rbs[0]){int x=rbs[rbs[0]];vis[x]=0;val[x]=0;vec[x].clear();dp[x][1].clear();dp[x][2].clear();rbs[0]--;}s=0;return ans;}
}
namespace T1{int head[N<<1],tot,num,size[N<<1],sum,nowroot,root,ga;bool jin[N<<2];ll deep[N<<1],ans;struct edge{int n,to;ll l;}e[N<<2];vector<int>vec[N];vector<ll>val[N];inline void add(int u,int v,ll l){vec[u].push_back(v);val[u].push_back(l);}vector<int>::iterator it;inline void add2(int u,int v,ll l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=l;}void dfs(int u,int fa){int now=0;for(int i=0;i<vec[u].size();++i){int v=vec[u][i];ll va=val[u][i];if(v==fa)continue;dfs(v,u);if(!now){add2(u,v,va);now=u;}else{++num;add2(now,num,0);add2(num,v,va);now=num;}}}void getroot(int u,int fa){size[u]=1;for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa&&!jin[i]){int v=e[i].to;getroot(v,u);size[u]+=size[v];if(max(size[v],sum-size[v])<nowroot){nowroot=max(size[v],sum-size[v]);root=i;ga=size[v];}}}void getdeep(int u,int fa,int tag){if(u<=n)a[++s]=node{tag,u,deep[u]};for(int i=head[u];i;i=e[i].n)if(!jin[i]&&e[i].to!=fa){int v=e[i].to;deep[v]=deep[u]+e[i].l;getdeep(v,u,tag);}}void solve(int now,int sm){if(sm==1)return;root=tot+1;nowroot=2e9;sum=sm;getroot(now,0);jin[root]=jin[root^1]=1;int x=e[root].to,y=e[root^1].to,sumx=ga,sumy=sm-ga;deep[x]=0;deep[y]=0;getdeep(x,y,1);getdeep(y,x,2);ans=max(T2::work()+e[root].l,ans);solve(x,sumx);solve(y,sumy);}inline void work(){num=n;tot=1;dfs(1,0);solve(1,num);printf("%lld",ans);}
}
int main(){n=rd();int u,v;ll w;for(int i=1;i<n;++i){u=rd();v=rd();w=rd();T1::add(u,v,w);T1::add(v,u,w);}for(int i=1;i<n;++i){u=rd();v=rd();w=rd();T2::add(u,v,w);T2::add(v,u,w);}for(int i=1;i<n;++i){u=rd();v=rd();w=rd();T3::add(u,v,w);T3::add(v,u,w); }T3::prework();T2::prework();T1::work();return 0;
}

转载于:https://www.cnblogs.com/ZH-comld/p/10388820.html

[WC2018]通道相关推荐

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

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

  2. ZJOI2019一轮停课刷题记录

    Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...

  3. 疾速体验!865主板选购指南

    Intel在业界的影响力是毋庸置疑的,无论是CPU还是芯片组,每款产品都受到强烈关注,而Intel往往也能通过其产品缔造业界标准,带动PC硬件到达一个新的高度.去年五月,P4 CPU的前端总线提升到了 ...

  4. 【WC2018】通道【边分治】【虚树】【树的直径】

    题意:给三棵基于同一点集的带边权的树,边权非负,求两点间三棵树上距离之和的最大值. n≤105n\leq 10^5n≤105 一句话题解:在第一棵树上做边分治,丢到第二棵树上建虚树,在虚树上根据第三棵 ...

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

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

  6. 「WC2018」通道

    没有代码能力... LOJ #2339 Luogu P4220 UOJ #347 题意 给定三棵树$ T1,T2,T3$,求一个点对$ (x,y)$使得$ T1.dist(x,y)+T2.dist(x ...

  7. Go 知识点(07)— 对已经关闭通道进行读写

    今天我们来看下对已经关闭通道进行读写会发生什么情况. 1. 对已关闭通道进行写操作 看下面代码会输出什么结果? func main() {ch := make(chan string, 1)close ...

  8. Python+OpenCV 图像处理系列(5)—— 图像 ROI 操作及通道的拆分合并

    1. 图像 ROI 有时你需要对一幅图像的特定区域进行操作.例如我们要检测一副图像中眼睛的位置,我们首先应该在图像中找到脸,再在脸的区域中找眼睛,而不是直接在一幅图像中搜索.这样会提高程序的准确性和性 ...

  9. Go 学习笔记(25)— 并发(04)[有缓冲/无缓冲通道、WaitGroup 协程同步、select 多路监听通道、close 关闭通道、channel 传参或作为结构体成员]

    1. 无缓冲的通道 无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道. 这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能 ...

最新文章

  1. 解题报告 - 牛客练习赛63 C - 牛牛的揠苗助长(货仓选址+二分)
  2. LintCode 1.A+B的问题
  3. 动态修改数据窗口的数据源
  4. 168. Leetcode 134. 加油站 (贪心算法-模拟题目)
  5. apache apollo php,php windows環境 安裝 Apache-apollo + phpMQTT 實現發送 MQTT
  6. 蔬菜大棚原理_天津大棚报价大棚的造价、温室大棚
  7. 计算机基础在线阅读,TOP16[定稿]计算机基础教案(上下册).doc文档免费在线阅读...
  8. C# Cache何时使用及使用方法
  9. python学习——matplotlib库——条形图(横版和竖版)
  10. CentOS 7.5 yum 安装 Git
  11. 苏州大学与东华大学计算机哪个好,选哪所大学好?东华大学和苏州大学对比
  12. 软件开发人员需要记住的10个操作系统概念(译)
  13. iPhones刷机怎么备份微信记录 iPhone手机微信记录怎么备份
  14. 淘宝关键词搜索采集商品价格销量接口分析商品价格走势(商品列表接口,商品销量接口,商品价格接口,分类ID采集精准商品数据接口)接口代码对接流程
  15. 详解CAN总线:CAN总线通信优先级机制
  16. 大数据测试是什么意思?测试分析方法有哪些?
  17. easyui datagrid th标签列数字保留2位小数
  18. 设计模式的六大设计原则
  19. 大学生研究生毕业找工作,该选择哪个方向?
  20. Protege使用入门

热门文章

  1. IOS开发笔记 - 基于SDWebImage的网络图片加载处理
  2. 【Java基础】重写与重载
  3. unicode 版本 delphi (如XE2)的 TBytes 转换为 AnsiString
  4. GridView列表数据的添加
  5. 不忘本~委托和事件(续)
  6. Asp.net MVC防止图片盗链的实现方法,通过自定义RouteHandler来操作
  7. 关于 DOM 操作的几个类型
  8. java集合框架类源代码阅读体会
  9. ErWin简单使用说明
  10. 现在c++都转go了