题目描述

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. canvas 时钟动画

    平时在公司不忙的时候,就喜欢写一些小效果什么的,一来复习复习,二来可以发现一些问题. 今天在群里看别人发了一手表的图片,卧槽...妥妥的工作好多年的节奏,后来想想还是做好自己的事情算了,想那多干啥,就 ...

  2. 会话固定攻击 - yxcms session固定漏洞

    目录 会话固定攻击 e.g. yxcms session固定攻击 分析 了解更多 会话固定攻击 Session fixation attack(会话固定攻击)是利用服务器的session不变机制,借他 ...

  3. ibm z14大型主机介绍

    IBM z14™大型主机 (z14)被设计为数字经济中值得信任的基础架构.它提供  特性和功能以满足对于新服务和更佳客户体验的需求,同时保护日益  增长的数据量,并遵从日益复杂的法规.IBM z14 ...

  4. PHAR系列之导言

    由于之前都是在现成的PHP框架下写web项目,一般都只专注于框架内部的使用跟优化.但是对于一些PHP很有特色的功能跟特性很少过多的深入了解.最近做项目调研的时候偶尔注意到一个PHP中一个比较常见的概念 ...

  5. IDF-cookie欺骗

    原题链接:http://ctf.idf.cn/game/web/40/index.php 进入题目,发现一个长字符串,放到md5.base64均无意义. 观察地址栏,发现有两个参数,line和file ...

  6. 关于 pyspider Web预览界面太小的解决方法

    本人最近在学习pyspider时,遇到Web预览界面太小而无法很好的进行开发,于是在网上搜索解决方法. 准备: css代码: body{margin:;padding:;height:%;overfl ...

  7. Hive:ORC File Format存储格式详解

    一.定义 ORC File,它的全名是Optimized Row Columnar (ORC) file,其实就是对RCFile做了一些优化. 据官方文档介绍,这种文件格式可以提供一种高效的方法来存储 ...

  8. TPYBoard开发板搭建,实现隐秘通信

    一.准备工作 lTPYBoard v102(简称v102) 1块 lTPYBoard v202(简称v202) 1块 l杜邦线.MicroUSB数据线 若干 (成本100元以内,某宝上可以买到) 附上 ...

  9. Each path can be reduced to a simple path

    Recently, I made a small conclusion, but I found it is found and well-founded in some textbook. So I ...

  10. 菜鸟学习计划浅谈之Linux系统

    人这一生都是在不断地学习,不断地进步中度过的,刚开始学习任何一门知识的时候,我们都习惯性的称自己为菜鸟,觉得自己对这方面的知识欠缺,水平很low,我也是如此.但我擅长总结,对于自己学习的新知识,总结学 ...