[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]战略 ...
随机推荐
- 从分治算法到 Hadoop MapReduce
从分治算法说起 要说 Hadoop MapReduce 就不得不说分治算法,而分治算法其实说白了,就是四个字 分而治之 .其实就是将一个复杂的问题分解成多组相同或类似的子问题,对这些子问题再分,然后再 ...
- vulnhub writeup - 持续更新
目录 wakanda: 1 0. Description 1. flag1.txt 2. flag2.txt 3. flag3.txt Finished Tips Basic Pentesting: ...
- 【idea】Springboot整合jpa
第一步快速搭建springboot项目:在你建立的工程下创建 Module 选择Spring initializr创建. 第二步:修改包名.项目名.web项目打成war包.在Type处选择: Mave ...
- chrome总是提示“请停用开发者模式运行的扩展程序”
方法1:通过组策略的扩展白名单.要下载一个组策略管理模板 1.开始 -> 运行 -> 输入gpedit.msc -> 回车确定打开计算机本地组策略编辑器(通过Win + R快捷键可以 ...
- SAP CRM Installed Bases(IBase)简介
SAP CRM使用Installed Base(以下简称IBase)来组织服务相关对象并进行管理.因为我在最近的工作中经常接触这个概念,所以学习了一点相关文档.下面是文档的翻译. 本文链接:https ...
- 英语进阶系列-A06-本周总结
本周总结 目录Content 英语进阶系列-A01-再别康桥 英语进阶系列-A02-英语学习的奥秘 英语进阶系列-A03-英语升级练习一 英语进阶系列-A04-英语升级练习二 英语进阶系列-A05-英 ...
- 【Teradata SQL】创建数据库和表
1.数据库perm大小为10G Create database testbase as perm=10E9,spool=10E9; 2.创建物理表 create multiset table stg( ...
- python开发【lambda篇】
lambda 与 python 高级函数的配套使用 filter函数 过滤 __author__ = "Tang" # filter(lambda, []) people = [' ...
- JavaScript面向对象—基本数据类型和引用数据类型的区别和变量及作用域(函数和变量)
基本类型和引用类型的值 ECMAScript 变量可能包含两种不同的数据类型的值:基本类型值和引用类型值. 基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置. 而引用 ...
- phpstudy运行时出现没有安装VC库
系统默认的VC库是安装在C:\Program Files\Common Files\microsoft shared\VC的文件夹里,当运行PHP Study是出现如下的提示: 可以到下面的网站去下载 ...