[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]战略 ...
随机推荐
- python模块(os,sys,hashlib,collections)
列出目录下所有文件 os.listdir('dirname'):列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式返回. 创建文件夹: os.mkdir('文件夹') 创建文件夹 os ...
- c/c++ 网络编程 UDP 主机网络信息取得
网络编程 UDP 主机网络信息取得 1,if_nametoindex 通过网卡名字取得网卡编号 2,if_indextoname 通过网卡编号取得网卡名字 #include <stdio.h&g ...
- SSH鞋贸商城的设计与实现
目录 应用技术 需求分析 总体设计 项目UI展示 一.应用技术 ①SSH SSH是 struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架.区别于 S ...
- Github: 从github上拉取别人的源码,并推送到自己的github仓库
比如说,将 https://github.com/lizhenliang/tomcat-java-demo 迁移到 https://github.com/lousia001/tomcat-java-d ...
- python接口自动化-get请求
一.环境安装 1.用pip安装requests模块 >>pip install requests 二.get请求 1. url 1.1: response 的返回内容还有很多信息,例 ...
- R语言学习——数组
> #数组(array)与矩阵类似,但维度可大于2.可通过array函数构建,形式如下:myarray<-array(vector,dimensions,dimnames)> #其中 ...
- 如何在本地搭建DVWA环境
如何在本地搭建DVWA环境 1.工具下载: (1)phpStudy: http://phpstudy.php.cn/download.html (2)DVWA:http://www.dvwa.c ...
- 【P2015】二叉苹果树 (树形DP分组背包)
题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 现在这颗树枝条太多了,需要剪枝.但是 ...
- .net core下用HttpClient和asp.net core实现https的双向认证
关于https双向认证的知识可先行google,这时矸接代码. 为了双向认证,我们首先得准备两个crt证书,一个是client.crt,一个是server.crt,有时为了验证是否同一个根证书的验证, ...
- Mysql数据的增删改查
一 介绍 MySQL数据操作: DML 在MySQL管理软件中,可以通过SQL语句中的DML语言来实现数据的操作,包括 使用INSERT实现数据的插入 UPDATE实现数据的更新 使用DELETE实现 ...