\([WC2018]\)通道(虚树,边分练习)

感受码题的快感

这段时间真的是忙忙忙忙忙,省选之前还是露个脸,免得以后没机会了。

但是我感觉我的博客真的没啥人看,虽然我挺想要有人看的,但是自己真的没啥时间写优质博客,而且最主要的是我做的题大佬们都做过。

现在这样还不如转肯竞。。。

不管怎样,\(mona\),在努力点吧,拜托了。

拜托了。

进入正题:思路讲解

这题还是很有意思的。

题目大概是给你三棵树,你需要找到一个点对使得他们在三棵树上的路径长度加起来要最大。

重新理一下思路。

像这样的题,给定三棵树,如果不能通过一些奇妙的转化来变成熟悉的东西的话,我们就要考虑像三维偏序那样一维一维处理影响。比如这题,很显然的,三棵树之间并没有关联,类似于需要考虑三个维度,那么我们就只能如上考虑。

首先因为是树上路径问题,我们先考虑树分治,首当其冲的应该是点分治,考虑过某一点的路径,那么第一棵树的路径长度考虑每个点与重心的距离,这样一来就相当于当前分治考虑的所有点都获得了一个点权,也就是原问题在第一棵树需要考虑的路径长度转化为了每个点固定的点权,这个点权在三棵树中都是适用的,因此这样就消去了一维的贡献。

考虑到第二棵树,将第一棵树里面考虑的点拿出来,比较显然的是建一棵虚树出来,这样复杂度就不会受到影响,那么我们考虑在虚树上计算第二棵树的贡献,思考一下能不能将第二棵树的影响也化为点权的形式呢?将距离公式拆开,发现是可以看成两个点的点权加上\(lca\)的点权,因为存在\(lca\)所以还是受第二棵树的形态的影响。回顾问题是要我们求带权直径,在消除了两维的情况下带权直径是可以合并的,那么我们在第二棵树上\(Dfs\),每次合并当前点子树的路径,相当于枚举了\(lca\),距离公式中剩下的部分转化为点权,这样一来合并子树在第三棵树中就可以形象的表示为,每次给定若干点集,依次合并其中的带权直径。

然而合并的时候是有限制的,在最里层考虑合并的时候,产生贡献的点首先不能是第二棵树中同一个子树内的点,其次不能是第一棵树中同一个子树的点,第二个好说,每一个点集合并上来本来就是分开的,在只在合并不同点集的时候贡献给答案就行了,而第一个,你只能对于每个子树的点集都维护一下带权直径,再在需要的时候考虑合并。

这样点分的问题就出来了,子树太多,如果直接维护的话复杂度爆炸。

这时候我们考虑边分,如果用点分的那套写法直接改边分的话,菊花图卡到爆炸,所以我们需要对树进行一些魔改,具体来说就是建一些虚点来使得树的形态更加“好看”,利用虚点,以类似线段树的方式将同属于一个点的儿子合并在一起,适当设置点权和边权能使得这些虚点不影响树的相关信息,一般来说,点权随父亲,边权放到每个儿子的新父边上。复杂度不会证,都说是\(O(nlogn)\)的。

这样一来,我们每次就只需要考虑两棵子树了,直接维护即可。

虚树的作用

(这道题中)虚树的作用为将复杂度与关键信息数绑定,简单粗暴地将有用信息提取。

一般涉及到关键点的时候可以考虑用虚树,即每次只需要考虑某些点的时候。

这个一般还是挺裸的,值得注意的是,有些虚树的形态可能会发生变化,这个随机应变就好。

至于构建虚树的过程大概是用栈模拟最右边一条链的加入过程,好好模拟一下就好了。

边分的作用

上文有提到,它的优势主要体现在每次只需要合并两棵子树。

然后实现方面有需要注意的地方。

首先,重构树的时候注意点数以及数组大小,正反边的对应关系以及边权点权的设定。

其次我认为比较好的实现方式是链式前向星存边,再利用其对于边的编号进行标记等操作。

其他的按照点分做即可。

关于码题

模块化编程是一定不能少的,如果写乱了就完蛋了。

对于我来说,多留几个换行方便代码之间的跳跃(习惯\(Ctrl+\)上下跳跃),并且多留几个调试端口,一般的码题,想的慢写的慢不算什么,想和写最多就一两个小时,但是调起来很有可能调死人,所以一定不要吝啬思考和写代码的时间,有可能一时的疏忽会造成几小时的损失。

调试端口是用来补救的,犯错是难免的事,一般先划分为若干子问题,再逐块调试。

调试时间比较久也是很容易发生的事情,一定要保持思路的清晰,自己要测试的是什么,有可能会出什么问题。这时候如果脑袋有点蒙建议洗个脸。

还有就是如果检查几遍发现代码无误,应该重新检视思路,事实上代码出问题调不出来的情况很少见,更有可能的是你细节上忽略了什么问题。

大概就这样,下面是代码。(话说\(C++11\)真的好用)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector> #define N 110000
#define ll long long
#define RG register
#define pb push_back
#define pr pair<int,ll>
#define mkp make_pair
#define fi first
#define sc second
#define ED 19
#define ds3 Tree3::Dis using namespace std; inline ll read(){
RG ll x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
} int n; ll ans;
namespace Tree3{
int dfn[N],cnt,dep[N],f[ED][N<<1],top,Log[N<<1],to[N<<1]; ll ds[N]; vector<pr> V[N];
inline int Get(int i,int j) { return dep[i]<dep[j]?i:j; }
inline int Lca(int i,int j){ i=to[i],j=to[j]; if(i>j) swap(i,j);
int len=j-i+1,Lg=Log[len]; return Get(f[Lg][i],f[Lg][j-(1<<Lg)+1]);
}
inline ll Dis(int u,int v) { int lca=Lca(u,v); return ds[u]+ds[v]-(ds[lca]<<1); }
inline void Dfs(int k,int fa){
dfn[k]=++cnt,f[0][++top]=k,dep[k]=dep[fa]+1,to[k]=top;
for(auto i:V[k]){
int en=i.fi; ll w=i.sc; if(en==fa) continue ;
ds[en]=ds[k]+w,Dfs(en,k),f[0][++top]=k;
} return ;
}
inline void Init(){
for(RG int i=1;i<n;++i){
int x=read(),y=read(); ll w=read();
V[x].pb(mkp(y,w)),V[y].pb(mkp(x,w));
} Dfs(1,0); for(RG int i=2;i<=top;++i) Log[i]=Log[i>>1]+1;
for(RG int i=1;i<ED;++i)
for(RG int j=1;j+(1<<i)-1<=top;++j)
f[i][j]=Get(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
} namespace Tree2{
int st[N],tot,dfn[N],cnt,dep[N],f[ED][N<<1],top,Log[N<<1],to[N<<1];
ll ds1[N],ds2[N]; vector<pr> V[N];
inline int Get(int i,int j) { return dep[i]<dep[j]?i:j; }
inline int Lca(int i,int j){ i=to[i],j=to[j]; if(i>j) swap(i,j);
int len=j-i+1,Lg=Log[len]; return Get(f[Lg][i],f[Lg][j-(1<<Lg)+1]);
}
inline void Dfs(int k,int fa){
dfn[k]=++cnt,f[0][++top]=k,dep[k]=dep[fa]+1,to[k]=top;
for(auto i:V[k]){
int en=i.fi; ll w=i.sc; if(en==fa) continue ;
ds2[en]=ds2[k]+w,Dfs(en,k),f[0][++top]=k;
} return ;
}
inline void Init(){
for(RG int i=1;i<n;++i){
int x=read(),y=read(); ll w=read();
V[x].pb(mkp(y,w)),V[y].pb(mkp(x,w));
} Dfs(1,0); for(RG int i=2;i<=top;++i) Log[i]=Log[i>>1]+1;
for(RG int i=1;i<ED;++i)
for(RG int j=1;j+(1<<i)-1<=top;++j)
f[i][j]=Get(f[i-1][j],f[i-1][j+(1<<(i-1))]);
} int Rt,tmp[N],imp[N],tt; vector<int> s[N]; ll ans; pair<int,int> Mx[N][2];
inline ll Get(int u,int v,ll now){
if(!u||!v) return -1e18;
return ds1[u]+ds1[v]+ds2[u]+ds2[v]-(now<<1)+ds3(u,v);
}
inline pr Maxp(pr x,pr y,ll now){
int A=x.fi,B=x.sc,C=y.fi,D=y.sc; ll tmp=0,Max=0;
tmp=Get(B,C,now); if(tmp>Max) Max=tmp,x=mkp(B,C);
tmp=Get(B,D,now); if(tmp>Max) Max=tmp,x=mkp(B,D);
tmp=Get(A,C,now); if(tmp>Max) Max=tmp,x=mkp(A,C);
tmp=Get(A,D,now); if(tmp>Max) Max=tmp,x=mkp(A,D);
tmp=Get(A,B,now); if(tmp>Max) Max=tmp,x=mkp(A,B);
tmp=Get(C,D,now); if(tmp>Max) Max=tmp,x=mkp(C,D); return x;
}
inline ll Maxs(pr x,pr y,ll now){
int A=x.fi,B=x.sc,C=y.fi,D=y.sc; ll tmp=0,Max=0;
tmp=Get(B,C,now); if(tmp>Max) Max=tmp;
tmp=Get(B,D,now); if(tmp>Max) Max=tmp;
tmp=Get(A,C,now); if(tmp>Max) Max=tmp;
tmp=Get(A,D,now); if(tmp>Max) Max=tmp; return Max;
}
inline void Dfs2(int k){
Mx[k][0]=Mx[k][1]=mkp(0,0); if(imp[k]) Mx[k][imp[k]-1]=mkp(k,k);
for(int en:s[k]){
Dfs2(en);
ans=max(ans,Maxs(Mx[k][0],Mx[en][1],ds2[k]));
ans=max(ans,Maxs(Mx[k][1],Mx[en][0],ds2[k]));
Mx[k][0]=Maxp(Mx[k][0],Mx[en][0],ds2[k]);
Mx[k][1]=Maxp(Mx[k][1],Mx[en][1],ds2[k]);
} return ;
}
inline void Clear(int k) { imp[k]=0; for(int i:s[k]) Clear(i); s[k].clear(); }
inline bool cmp(int x,int y) { return dfn[x]<dfn[y]; }
inline void Insert(int u){
if(!tt) { tmp[++tt]=u; return ; }
int lca=Lca(u,tmp[tt]); if(lca==tmp[tt]) { tmp[++tt]=u; return ; }
while(tt>1&&dfn[tmp[tt-1]]>=dfn[lca]) s[tmp[tt-1]].pb(tmp[tt]),--tt;
if(tmp[tt]!=lca) s[lca].pb(tmp[tt]),tmp[tt]=lca;
tmp[++tt]=u; return ;
}
inline ll Calc(){
sort(st+1,st+1+tot,cmp),ans=tt=0;
for(RG int i=1;i<=tot;++i) Insert(st[i]);
while(tt>1) s[tmp[tt-1]].pb(tmp[tt]),--tt;
Rt=tmp[tt],Dfs2(Rt),Clear(Rt),tot=0; return ans;
}
} namespace Tree1{
vector<pr> V[N<<2],si[N<<2]; int n,cp,top,first[N<<2];
struct mona { int nxt,en; ll w; } s[N<<3];
inline void Insert(int x,int y,ll w) { s[++top]=(mona) { first[x],y,w },first[x]=top; }
inline void Dfs(int k,int fa){
for(auto i:V[k]){
int en=i.fi; if(en==fa) continue ;
si[k].pb(mkp(en,i.sc)),Dfs(en,k);
} return ;
}
inline void ReBuild(){
for(RG int i=1;i<=n;++i){
int sz=si[i].size();
if(sz<=2)
for(auto j:si[i]){
int en=j.fi; ll w=(en<=cp)*j.sc;
Insert(i,en,w),Insert(en,i,w);
}
else{
int A=++n,B=++n,t=0; Insert(i,A,0),Insert(A,i,0);
Insert(i,B,0),Insert(B,i,0);
for(auto j:si[i]) if((t++)&1) si[A].pb(j); else si[B].pb(j);
}
}
} int vis[N<<2],siz[N<<2],Sz,Rt,Max;
inline void Getroot(int k,int fa){
siz[k]=1;
for(RG int i=first[k];i;i=s[i].nxt){
int en=s[i].en; if(en==fa||vis[i>>1]) continue ;
Getroot(en,k),siz[k]+=siz[en];
int Mx=max(siz[en],Sz-siz[en]);
if(Mx<Max) Rt=i,Max=Mx;
} return ;
}
inline void Dfs(int k,int fa,ll ds,int op){
if(k<=cp) Tree2::st[++Tree2::tot]=k,Tree2::ds1[k]=ds,Tree2::imp[k]=op;
for(RG int i=first[k];i;i=s[i].nxt){
int en=s[i].en; if(en==fa||vis[i>>1]) continue ;
Dfs(en,k,ds+s[i].w,op);
}
}
inline void Divide(int u){
if(!u) return ; int x=s[u].en,y=s[u^1].en;
if((x==8210&&y==100002)||(x==8210&&y==100002))
++x,--x;
vis[u>>1]=1,Dfs(x,0,0,1),Dfs(y,0,0,2);
ll tmp=Tree2::Calc()+s[u].w; ans=max(tmp,ans);
int A=siz[x]<siz[y]?siz[x]:Sz-siz[y],B=siz[y]<siz[x]?siz[y]:Sz-siz[x];
Max=1e9,Sz=A,Rt=0,Getroot(x,0),Divide(Rt);
Max=1e9,Sz=B,Rt=0,Getroot(y,0),Divide(Rt);
}
inline void Init(){ cp=n,top=1;
for(RG int i=1;i<n;++i){
int x=read(),y=read(); ll w=read();
V[x].pb(mkp(y,w)),V[y].pb(mkp(x,w));
}
}
inline void Begin() { Dfs(1,0),ReBuild(),Max=1e9,Sz=n,Getroot(1,0),Divide(Rt); }
} int main(){
n=Tree1::n=read(),Tree1::Init(),Tree2::Init(),Tree3::Init();
Tree1::Begin(),printf("%lld\n",ans);
}

随机推荐

  1. assert出问题了?

    刚学习Objective-C那会儿,还不太了解这个世界的惯用法,所以有些地方使用了C/C++的方式,虽然后来做过一定的修改, 但是项目中还是遗留了一些无关紧要的C/C++代码.比如对断言的运用. as ...

  2. fedora18 You might need to install dependency packages for libxcb.

    22 down vote The page Qt for X11 Requirements lists some packages required to build Qt on Debian. Th ...

  3. delphi中的inpubox,如何能控制它的位置? 10

    https://zhidao.baidu.com/question/153270855.html delphi中的inpubox,如何能控制它的位置? 10 RT ! 前辈!最好你就把那代码都拿出来吧 ...

  4. 从DT时代云栖大会聊聊恒生电子

    从IT到DT,除了HOMS和配资,本文结合互联网金融的背景,帮助读者对恒生电子600570了解更多. 马云在2015杭州·云栖大会Computing Conference发表致辞时多次强调DT(Dat ...

  5. Python uuid库中 几个uuid的区别

    在用到uuid库的时候,发现uuid有很多个,比较好奇,就查了一下他们的区别 uuid1()——基于时间戳 uuid2()——基于分布式计算环境DCE(Python中没有这个函数) uuid3()—— ...

  6. G2 基本使用 折线图 柱状图 饼图 基本配置

    G2的基本使用 1.浏览器引入  <!-- 引入在线资源 --> <script src="https://gw.alipayobjects.com/os/lib/antv ...

  7. vue集成汉字转拼音或提取首字母

    需求:             有时我们为了节省用户的维护量,需要根据中文生成出相应的拼音和缩写 解决:            此方法是利用汉字和Unicode编码对应找到相应字母 一.编写汉字和编码 ...

  8. ElasticSearch 基础 2

    ================================== 高级查询 =========================== ========== 子条件查询 =========== _sc ...

  9. web 前端1 html5基础

    HTML web sockent 实例 import socket def handle_request(client): buf = client.recv(1024) client.sendall ...

  10. Ubuntu解决Nvidia驱动缺失导致的HDMI无法输出问题

    朋友的电脑是联想Y7000,因为Nvidia驱动的问题几次头疼脑大.这次是出现了HDMI在Windows 10下输出正常,而Ubuntu系统下无法输出. 原因分析 如果通过HDMI去连接显示器以后会发 ...