[WC2018]通道
题目描述
题解
解法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]通道的更多相关文章
- [WC2018]通道——边分治+虚树+树形DP
题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...
- $[WC2018]$通道(虚树,边分练习)
\([WC2018]\)通道(虚树,边分练习) 感受码题的快感 这段时间真的是忙忙忙忙忙,省选之前还是露个脸,免得以后没机会了. 但是我感觉我的博客真的没啥人看,虽然我挺想要有人看的,但是自己真的没啥 ...
- UOJ347 WC2018 通道 边分治、虚树
传送门 毒瘤数据结构题qwq 设三棵树分别为$T1,T2,T3$ 先将$T1$边分治,具体步骤如下: ①多叉树->二叉树,具体操作是对于每一个父亲,建立与儿子个数相同的虚点,将父亲与这些虚点穿成 ...
- 洛谷P4220 [WC2018]通道(边分治+虚树)
题面 传送门 题解 代码不就百来行么也不算很长丫 虽然这题随机化贪心就可以过而且速度和正解差不多不过我们还是要好好学正解 前置芝士 边分治 米娜应该都知道点分治是个什么东西,而边分治,顾名思义就是对边 ...
- bzoj5152 [Wc2018]通道
题目链接 正解:不会做. 写一个爬山算法就过官方数据了(逃 具体来说就是每次随机一个根,然后迭代找最长路的那个点. 多随机几次取$max$就行了.正解以后再补.. #include <bits/ ...
- [WC2018]通道(乱搞,迭代)
[洛谷题面]https://www.luogu.org/problemnew/show/P4221 这个题以及[CTSC2018 暴力写挂]都有类似的乱搞做法能通过考场数据. 具体搞法就是随一个起点, ...
- ZJOI2019一轮停课刷题记录
Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...
- WC 2018 题解
WC 2018 题解 一些感受.jpg 题目难度相较前些年会相对简单一点?(FAKE.jpg 平均码量符合WC风格?(甚至更多一点 出题人良心! [WC2018] 通道 一个不知道对不对的$\log ...
- 仙人掌&圆方树
仙人掌&圆方树 Tags:图论 [x] [luogu4320]道路相遇 https://www.luogu.org/problemnew/show/P4320 [ ] [SDOI2018]战略 ...
随机推荐
- [安卓]ListView 与 RecyclerView的比较
ListView与RecyclerView在在app应用非常广泛,相对于其他的view(button textview)来说比较复杂,接下来我将讲一下创建的流程以及两者的不同. 代码来自<第一行 ...
- 关于使用阿里OSS服务搭建图床和使用PicGO上传图片到图床
最近喜欢上了使用markdown来写博客,可是markdown的图片却是本地的,如果我要发博客,那么又要重复截图了.于是干脆弄了个图床,本地截图的时候上传到图床,markdown中的代码结果也是图床里 ...
- Spark之Pipeline处理模式
一.简介 Pipeline管道计算模式:只是一种计算思想,在数据处理的整个流程中,就想水从管道流过一下,是顺序执行的. 二.特点 1.数据一直在管道中,只有在对RDD进行持久化[cache,persi ...
- macos 远程 windows 10
使用的工具:Parallels Client 免费,可以从windows store 免费下载: 只有这个最好用,希望能帮到你.和原生的windows 远程桌面功能很像:
- ibm z14大型主机介绍
IBM z14™大型主机 (z14)被设计为数字经济中值得信任的基础架构.它提供 特性和功能以满足对于新服务和更佳客户体验的需求,同时保护日益 增长的数据量,并遵从日益复杂的法规.IBM z14 ...
- XML详解二XML的解析与创建
XML用来传输和存储数据,如何解析获取到的XML文本呢? 一.解析XML 创建demo.xml文件: <?xml version="1.0" encoding="U ...
- Linux之Samba部署
1.Samba介绍 Samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件,由服务器及客户端程序构成,SMB(Server Messages Block,信息服务块)是一种 ...
- 数据流中的第k大元素的golang实现
设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中 ...
- 深入Ambari Metrics 机制分析
0.简介 Ambari作为一款针对大数据平台的运维管理工具,提供了集群的创建,管理,监控,升级等多项功能,目前在业界已经得到广泛使用. Ambari指标系统( Ambari Metrics Syste ...
- mysql 有没有参数都报错“mysql: unknown option”
报错: [root@XXXX tmp]# mysql -uroot -pmysql: unknown option '--You have new mail in /var/spool/mail/ro ...