题目描述

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. iOS----------弹窗动画

    - (void)animationAlert:(UIView *)view { CAKeyframeAnimation *popAnimation = [CAKeyframeAnimation ani ...

  2. Android远程桌面助手(Build 0662)

    ARDC Build 0662, Jul 19, 2017 OPT: 1440*2560及以下分辨率设备,帧速能稳定在20帧~25帧 FIX: 拖拽文件的路径中包含空格的处理 ADD: 支持Ctrl+ ...

  3. nginx 防止盗链

    1.测试盗链(www.html2.com 盗取 www.html5.com的图片) 2.防止盗链 符合盗链 —— 重写 说明:if ($invalid_referer) {,if的后面是有空格的,如果 ...

  4. linux缺页异常处理--内核空间

    缺页异常被触发通常有两种情况-- 程序设计的不当导致访问了非法的地址 访问的地址是合法的,但是该地址还未分配物理页框. 下面解释一下第二种情况,这是虚拟内存管理的一个特性.尽管每个进程独立拥有3GB的 ...

  5. SSH鞋贸商城的设计与实现

    目录 应用技术 需求分析 总体设计 项目UI展示 一.应用技术 ①SSH SSH是 struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架.区别于 S ...

  6. Java实现Sunday百万级数据量的字符串快速匹配算法

    背景       在平时的项目中,几乎都会用到比较两个字符串时候相等的问题,通常是用==或者equals()进行,这是在数据相对比较少的情况下是没问题的,当数据库中的数据达到几十万甚至是上百万千万的数 ...

  7. Redis内存优化memory-optimization

    https://redis.io/topics/memory-optimization  官方文档 一.特殊编码: 自从Redis 2.2之后,很多数据类型都可以通过特殊编码的方式来进行存储空间的优化 ...

  8. DeveloperGuide Hive UDAF

    Writing GenericUDAFs: A Tutorial User-Defined Aggregation Functions (UDAFs) are an excellent way to ...

  9. realm swift调研--草稿

    realm swift调研: After you have added the object to the Realm you can continue using it, and all chang ...

  10. EF Core 遇到“可能会导致循环或多重级联路径”

    在ef core中你可能会设计这样一个实体: public class Customer : Entity,IMustHaveTenant, IHasCreationTime { public Cus ...