题目描述

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[][N],n,ans;
bool vis[N];
inline ll rd(){
ll x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct Edge{
int head[N],tot;
struct ljb{int n,to;ll l;}e[N<<];
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[];
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=;x<=n;++x){
dis[][x]=dis[][x]=dis[][x]=;
dfs(x,,);dfs(x,,);dfs(x,,);
for(int i=;i<=n;++i){
ans=max(ans,dis[][i]+dis[][i]+dis[][i]);
}
}
}
int main(){
srand();
double start=get_clock;
n=rd();ll u,v,w;
for(int i=;i<n;++i){
u=rd();v=rd();w=rd();
edge[].add(u,v,w);
}
for(int i=;i<n;++i){
u=rd();v=rd();w=rd();
edge[].add(u,v,w);
}
for(int i=;i<n;++i){
u=rd();v=rd();w=rd();
edge[].add(u,v,w);
}
if(n<=){
BF();
}
else{
while(get_clock-start<=){
int x=rand()%n+;
while(vis[x])x=rand()%n+;
for(int j=;j<=;++j){
if(vis[x])break;
vis[x]=;
dis[][x]=dis[][x]=dis[][x]=;
dfs(x,,);dfs(x,,);dfs(x,,);
ll now=;
for(int i=;i<=n;++i){
ll num=dis[][i]+dis[][i]+dis[][i];
ans=max(ans,num);
if(num>now){now=num;if(!vis[i])x=i;}
}
}
}
}
cout<<ans;
return ;
}

这样做可以通过官方数据,但是在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=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);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[][N<<],tag[N],lo[N<<];
ll dis[N];
struct edge{int n,to;ll l;}e[N<<];
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[][++tag[]]=u;tag[u]=tag[];
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[][++tag[]]=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+];
return great(p[llo][x],p[llo][y-(<<llo)+]);
}
inline void prework(){
dfs(,);
for(int i=;(<<i)<=tag[];++i)
for(int j=;j<=tag[];++j)p[i][j]=great(p[i-][j],p[i-][j+(<<i-)]);///!!!!
for(int i=;i<=tag[];++i)lo[i]=lo[i>>]+;
}
inline ll calc(int x,int y){return dis[x]+dis[y]-*dis[getlca(x,y)];}
}
namespace T2{
int head[N],tot,p[][N<<],tag[N],lo[N<<],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<<];
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[];
inline bool empty(int x,int y){return (!x&&!y);}
inline void clear(){x[]=x[]=;}
}dp[N][];
void dfs(int u,int f){
p[][++tag[]]=u;tag[u]=tag[];dfn[u]=++dfn[];
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[][++tag[]]=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+];
return great(p[llo][x],p[llo][y-(<<llo)+]);
}
inline void prework(){
dfs(,);
for(int i=;(<<i)<=tag[];++i)
for(int j=;j+(<<i)-<=tag[];++j)p[i][j]=great(p[i-][j],p[i-][j+(<<i-)]);
for(int i=;i<=tag[];++i)lo[i]=lo[i>>]+;
}
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[]=u;
for(int i=;i<vec[u].size();++i){
int v=vec[u][i];
ddp(v);
for(int i=;i<=;++i)
for(int j=;j<;++j)
for(int k=;k<;++k){
int d=i==?:,x=dp[u][i].x[j],y=dp[v][d].x[k];
ans=max(ans,calc(x,y)-2ll*dis[u]);
} for(int i=;i<=;++i){
if(!dp[u][i].x[]&&!dp[u][i].x[]){
dp[u][i].x[]=dp[v][i].x[];
dp[u][i].x[]=dp[v][i].x[];continue;
}
if(!dp[v][i].x[]&&!dp[v][i].x[])continue;
int maxx=dp[u][i].x[],maxy=dp[u][i].x[];ll you=calc(maxx,maxy),bian=calc(dp[v][i].x[],dp[v][i].x[]);
if(bian>you)maxx=dp[v][i].x[],maxy=dp[v][i].x[],you=bian;
for(int j=;j<;++j)
for(int k=;k<;++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[]=maxx;dp[u][i].x[]=maxy;
}
}
}
inline ll work(){
ans=-inf;
sort(a+,a+s+);
for(int i=;i<=s;++i){vis[a[i].x]=a[i].tag;val[a[i].x]=a[i].y+dis[a[i].x];}
int start=;
st[top=]=rbs[rbs[]=]=;
if(a[].x==)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[]]=x;continue;}
while(top>){
int xx=st[top],yy=st[top-];
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[]]=lc;}
if(st[top]!=x){st[++top]=x;rbs[++rbs[]]=x;}
}
while(top>){vec[st[top-]].push_back(st[top]);top--;}
ddp();
while(rbs[]){
int x=rbs[rbs[]];
vis[x]=;val[x]=;vec[x].clear();
dp[x][].clear();dp[x][].clear();
rbs[]--;
}
s=;
return ans;
}
}
namespace T1{
int head[N<<],tot,num,size[N<<],sum,nowroot,root,ga;
bool jin[N<<];
ll deep[N<<],ans;
struct edge{int n,to;ll l;}e[N<<];
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=;
for(int i=;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,);
add2(num,v,va);
now=num;
}
}
}
void getroot(int u,int fa){
size[u]=;
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==)return;
root=tot+;nowroot=2e9;
sum=sm;getroot(now,);
jin[root]=jin[root^]=;
int x=e[root].to,y=e[root^].to,sumx=ga,sumy=sm-ga;
deep[x]=;deep[y]=;getdeep(x,y,);getdeep(y,x,);
ans=max(T2::work()+e[root].l,ans);
solve(x,sumx);solve(y,sumy);
}
inline void work(){
num=n;tot=;dfs(,);solve(,num);
printf("%lld",ans);
}
}
int main(){
n=rd();int u,v;ll w;
for(int i=;i<n;++i){
u=rd();v=rd();w=rd();
T1::add(u,v,w);T1::add(v,u,w);
}
for(int i=;i<n;++i){
u=rd();v=rd();w=rd();
T2::add(u,v,w);T2::add(v,u,w);
}
for(int i=;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 ;
}

[WC2018]通道的更多相关文章

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

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

  2. $[WC2018]$通道(虚树,边分练习)

    \([WC2018]\)通道(虚树,边分练习) 感受码题的快感 这段时间真的是忙忙忙忙忙,省选之前还是露个脸,免得以后没机会了. 但是我感觉我的博客真的没啥人看,虽然我挺想要有人看的,但是自己真的没啥 ...

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

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

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

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

  5. bzoj5152 [Wc2018]通道

    题目链接 正解:不会做. 写一个爬山算法就过官方数据了(逃 具体来说就是每次随机一个根,然后迭代找最长路的那个点. 多随机几次取$max$就行了.正解以后再补.. #include <bits/ ...

  6. [WC2018]通道(乱搞,迭代)

    [洛谷题面]https://www.luogu.org/problemnew/show/P4221 这个题以及[CTSC2018 暴力写挂]都有类似的乱搞做法能通过考场数据. 具体搞法就是随一个起点, ...

  7. ZJOI2019一轮停课刷题记录

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

  8. WC 2018 题解

    WC 2018 题解 一些感受.jpg 题目难度相较前些年会相对简单一点?(FAKE.jpg 平均码量符合WC风格?(甚至更多一点 出题人良心! [WC2018] 通道 一个不知道对不对的$\log ...

  9. 仙人掌&圆方树

    仙人掌&圆方树 Tags:图论 [x] [luogu4320]道路相遇 https://www.luogu.org/problemnew/show/P4320 [ ] [SDOI2018]战略 ...

随机推荐

  1. 2018年IOS/Android UI设计规范

    更多参考: 2017最新设计尺寸及规范 UI : 2018年IOS/Android UI设计规范 转载:https://www.jianshu.com/p/03e5cdd4ffd6

  2. python 图片在线转字符画预览

    文章链接:https://mp.weixin.qq.com/s/yiFOmljhyalE8ssAgwo6Jw 关于python图片转字符画,相信大家都不陌生,经常出现在 n个超有趣的python项目中 ...

  3. ES6使用的一些方法

    查找数组中符合条件的所有记录 var list=[ {id:1,name:"张三"}, {id:2,name:"李四"}, {id:3,name:"王 ...

  4. MFC映射

    所有CDC输出函数最终都会输出到物理平面(屏幕窗口.打印纸等).这些物理平面的单位量化往往多种多样,比如像素.打印点.英寸.毫米等等.这样可能会造成很多混乱,所以CDC输出对所有物理平面进行统一抽象化 ...

  5. sqlserver数据库导出表结构和表数据生成创建表和insert语句

    问题描述: 有时候我们只需要导出一张表和表数据到另外一个数据库,如果是备份整个库的话,就会很麻烦那样,没法满足需求. 解决方法: 以sqlserver2014为例:把MGActivity数据库的bat ...

  6. Python基础——1基础

    1.基础 输出 print(‘把子肉爱上热干面’,‘哈哈’)  # ‘,’输出为空格 输人 name = input(‘提示的内容’) /浮点除法  %.6f //地板除法  整除 %  取余 pyt ...

  7. JetBrains 注册码

    C40PF37RR0-eyJsaWNlbnNlSWQiOiJDNDBQRjM3UlIwIiwibGljZW5zZWVOYW1lIjoiemhhbmcgeW9uZyIsImFzc2lnbmVlTmFtZ ...

  8. 期末架构-Centos7

    00--linux运维架构演变过程 01--老男孩教育-CentOS6和7 01-笔记 安装系统 系统下载地址:http://mirrors.aliyun.com/centos/7/isos/x86_ ...

  9. python之pickle

    #!/usr/bin/python # -*- coding: UTF- -*- ''' ''' import pickle # pickle 只能Python识别 不适用于别的语言 li = [, ...

  10. C++ shared_ptr、unique_ptr、weak_ptr

    shared_ptr unique_ptr weak_ptr 内存泄漏 智能指针 引用计数 循环引用 reset