Kruskal 重构树

[您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树

kruskal是一个性质优秀的算法

加入的边是越来越劣的

科学家们借这个特点尝试搞一点事情。

kruskal求最小生成树的过程,如果把加入的一个边新建一个节点的话,并且把k1,k2的father设为新点的话,会得到一个2*n大小的树

实际上已经非常明白地表示kruskal这个过程了。这个树叫kruskal重构树

每个点的权值定义为所代表的边的权值。叶子节点权值最优。

由于贪心,所以树上所有点,从儿子到祖先权值单调不优。(这是最关键的性质)

树的结构我们非常熟悉

所以各种算法纷至沓来。

具体怎么用?

BZOJ3545 Peaks:

在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走。

现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。N<=1e5,M,Q<=5*1e5

(不强制在线的话,直接离线排序+线段树合并或者启发式合并。(类似“永无乡”))

强制在线呢?

kruskal重构树登场!

对于只走小于x的边,会得到一个可以到达的联通块。

最小生成树的边上走,保证这个联通块是极大的。

联通块高度的第k大就是答案。

发现这个联通块对应重构树的一个子树!

重构树上,从v开始往上倍增找到最后一个权值小于等于x的点p

p对应的子树的叶子就是所有的联通块的点。(有点类似SAM的fail树?)

所以区间第k大,用dfs序处理一下,+主席树维护即可。

NOI2018归程

[NOI2018]归程 

如果想到kruskal重构树(感觉学了应该都能想到,毕竟适用面也不是很广),那么这题就水了。

所能开车到达的集合里选择距离家最近的点下车。

kruskal重构树建出来(最大生成树),叶子赋予一个dis到1的距离。

dis用dij跑(因为出题人卡spfa)

然后dfs重构树处理信息,然后倍增处理询问。大功告成!

代码:

(很丑陋,见缝插针,而且mi、dep数组其实根本不需要)

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const int M=+;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll dis[N];
ll ans[*N];
int val[*N];
int n,m;
struct node{
int nxt,to;
int val;
}e[*N];
int hd[*N],cnt;
void add(int x,int y,int z){
e[++cnt].nxt=hd[x];
e[cnt].to=y;
e[cnt].val=z;
hd[x]=cnt;
}
int tot;
namespace dij{ bool vis[N];
struct po{
int x,d;
po(int xx,int dd){
x=xx;d=dd;
}
bool friend operator <(po a,po b){
return a.d>b.d;
}
};
priority_queue<po>q;
void dij(){
memset(vis,,sizeof vis);
memset(dis,0x3f,sizeof dis);
while(!q.empty()) q.pop();
dis[]=;
q.push(po(,));
while(!q.empty()){
po now=q.top();q.pop();
if(vis[now.x]) continue;
vis[now.x]=;
dis[now.x]=now.d;
for(reg i=hd[now.x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y]) continue;
q.push(po(y,dis[now.x]+e[i].val));
}
}
for(reg i=;i<=n;++i){
ans[i]=dis[i];
//mi[i][0]=0x3f3f3f3f;
}
}
} struct edge{
int x,y;
int l,a;
bool friend operator <(edge a,edge b){
return a.a>b.a;
}
}b[M];
//represent the value of edge
namespace kruscal{
int fa[*N];
int fin(int x){
return fa[x]==x?x:fa[x]=fin(fa[x]);
}
void main(){
for(reg i=;i<=n;++i) {
fa[i]=i;val[i]=0x3f3f3f3f;
}
tot=n;
// cout<<" tot "<<tot<<endl;
sort(b+,b+m+);
for(reg i=;i<=m;++i){
int k1=fin(b[i].x),k2=fin(b[i].y);
// cout<<" bb "<<tot<<" "<<i<<" : "<<b[i].x<<" "<<b[i].y<<" rt "<<k1<<" sand "<<k2<<endl;
if(k1!=k2){ ++tot;
fa[tot]=tot;
ans[tot]=inf; add(tot,k1,);
add(tot,k2,);
fa[k1]=fa[k2]=tot;
val[tot]=b[i].a;
}
}
} }
int fa[*N][];
int mi[*N][];
int dep[*N]; void dfs(int x,int d){
dep[x]=d;
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
fa[y][]=x;
mi[y][]=min(val[y],val[x]);
dfs(y,d+);
ans[x]=min(ans[x],ans[y]);
}
}
ll query(int x,int p){
for(reg j=;j>=;--j){
if(fa[x][j]){
if(mi[x][j]>p) x=fa[x][j];
}
}
return ans[x];
}
void clear(){
memset(fa,,sizeof fa);
memset(mi,0x3f,sizeof mi);
memset(dep,,sizeof dep);
memset(hd,,sizeof hd);
cnt=;
}
int main(){
int T;
rd(T);
while(T--){
rd(n);rd(m);
clear();
for(reg i=;i<=m;++i){
rd(b[i].x);rd(b[i].y);rd(b[i].l);rd(b[i].a);
add(b[i].x,b[i].y,b[i].l);
add(b[i].y,b[i].x,b[i].l);
}
dij::dij();
// cout<<" after dij "<<endl; memset(hd,,sizeof hd);
cnt=;
kruscal::main();
// cout<<" after kruc "<<endl; dfs(tot,);
// cout<<" after dfs "<<endl;
for(reg j=;j<=;++j){
for(reg i=;i<=tot;++i){
fa[i][j]=fa[fa[i][j-]][j-];
mi[i][j]=min(mi[i][j-],mi[fa[i][j-]][j-]);
}
} // cout<<" tot "<<tot<<endl;
// for(reg i=1;i<=tot;++i){
// cout<<" ii "<<i<<" val "<<val[i]<<" ans "<<ans[i]<<" fafa "<<fa[i][0]<<endl;
// }
int q,k,s;
rd(q);rd(k);rd(s);
ll las=;
int v,p;
while(q--){
rd(v);rd(p);
v=(v+k*las-)%n+;
p=(p+k*las)%(s+);
printf("%lld\n",las=query(v,p));
}
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2019/1/8 18:49:37
*/

这个算法适用面:
在线处理询问在经过小于(大于)某个边权的边所能到的集合里的信息。

主要性质是祖先权值的单调不优。

这个算法是对kruskal求最小生成树的操作过程的树形结构化,从而精确剖析过程,使得维护方便。

有异曲同工之妙的是动态点分治的分治树,也是对操作过程的树形结构化。

还有一个例题:(NOI和IOI都考了诶热度真高)

[IOI2018] werewolf 狼人

[IOI2018] werewolf 狼人


upda:2019.2.13

并查集重构树

除了Kruskal重构树之外,还有一个东西叫做并查集重构树

引入例题

n个点,q次操作,每次加入一条边(不会重边自环),或者查询两个点最早什么时候连通

n,q<=1e5

(某次jzoj考试题)

还是考虑具体维护出操作的结构

用按秩合并并查集,边权就是这次加入的边的时间值

发现,两个点第一次连通,一定是两个点并查集上路径最大值!

由于按秩合并,树高logn,可以暴力查询

如果是最后询问,可以建倍增数组,loglogn复杂度

对于一般的图,边权sort,然后按照上述做即可

毒瘤一些:

1.sort用松式基排

2.并查集按秩合并+路径压缩,另外开一个邻接表记录树

接近O(n)

性质:
1.越靠上权值越劣

2.两点间最大权值就是连通时间

比较优劣

并查集重构树:

劣势:子树不是一个区间(因为没有附加点)

优势:空间小,好写好调,两点间信息可以O(loglogn)快速求出。

kruskal重构树:

反过来。

劣势:难写一些,容易写错。

优势:多了附加点,经过小于vi的边到达的区域是子树,加上dfn序列很好维护。

[学习笔记]kruskal重构树 && 并查集重构树的更多相关文章

  1. 【学习笔记】可持久化并查集(BZOJ3673)

    好久之前就想学了 然后今天恰巧一道题需要用到就学了 前置芝士 1.主席树[可持久化数组] 2.并查集 如果你掌握了前面两个那么这个东西你就会觉得非常沙茶.. 构造 可持久化并查集 = 主席树  + 并 ...

  2. BZOJ3712[PA2014]Fiolki——并查集重构树

    题目描述 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤 ...

  3. 【BZOJ3712】Fiolki(并查集重构树)

    [BZOJ3712]Fiolki(并查集重构树) 题面 BZOJ 题解 很神仙的题目. 我们发现所有的合并关系构成了一棵树. 那么两种不同的东西如果产生反应,一定在两个联通块恰好联通的时候反应. 那么 ...

  4. 【web开发学习笔记】Structs2 Result学习笔记(二)动态结果集

    Result学习笔记(二) - 动态结果集     动态结果 一定不要忘了为动态结果的保存值设置set get方法 第一部分:代码 //前端 <% String context = reques ...

  5. CF1253F Cheap Robot(神奇思路,图论,最短路,最小生成树/Kruskal 重构树/并查集)

    神仙题. 先考虑平方级别的暴力怎么做. 明显答案有单调性,先二分 \(c\). 先最短路预处理 \(dis_u\) 表示 \(u\) 到离它最近的充电站的距离(一开始把 \(1\) 到 \(k\) 全 ...

  6. 洛谷P1991无线通讯网[kruskal | 二分答案 并查集]

    题目描述 国防部计划用无线网络连接若干个边防哨所.2 种不同的通讯技术用来搭建无线网络: 每个边防哨所都要配备无线电收发器:有一些哨所还可以增配卫星电话. 任意两个配备了一条卫星电话线路的哨所(两边都 ...

  7. cf827D Best Edge Weight (kruskal+倍增lca+并查集)

    先用kruskal处理出一个最小生成树 对于非树边,倍增找出两端点间的最大边权-1就是答案 对于树边,如果它能被替代,就要有一条非树边,两端点在树上的路径覆盖了这条树边,而且边权不大于这条树边 这里可 ...

  8. EF学习笔记-1 EF增删改查

    首次接触Entity FrameWork,就感觉非常棒.它节省了我们以前写SQL语句的过程,同时也让我们更加的理解面向对象的编程思想.最近学习了EF的增删改查的过程,下面给大家分享使用EF对增删改查时 ...

  9. Docker 与 K8S学习笔记(二十三)—— Kubernetes集群搭建

    小伙伴们,好久不见,这几个月实在太忙,所以一直没有更新,今天刚好有空,咱们继续k8s的学习,由于我们后面需要深入学习Pod的调度,所以我们原先使用MiniKube搭建的实验环境就不能满足我们的需求了, ...

随机推荐

  1. ubuntu18.04安装mongoDB 4.0

    STEP 1:  在终端输入GPK码 $  sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334B ...

  2. 车牌,车架号,VIN码毫秒识别技术,汽车后市场的春天到来了

    vin码(车架号)识别运用 不仅在制造.销售.保养.保险.车辆评估.交易环节会需要录入汽车的VIN码,在交通事故处理中,作为汽车身份唯一识别码,VIN码是处理事故的执法人员必须要记录的信息之一.随着汽 ...

  3. 转 gerrit

    开发环境 https://blog.csdn.net/u013207966/article/details/79112740 先记录下我的开发环境以及要正确安装gerrit需要用到的工具: Redha ...

  4. 【springmvc+mybatis项目实战】杰信商贸-6.重点知识回顾

    1.重点知识回顾 Maven1)覆盖仓库文件,实际企业开发,公司会架一个测试服务器,在测试服务器中架私服.我们开发人员的程序,都连接私服.当本地没有项目中要使用的jar,Myeclipse maven ...

  5. PHP autoload与spl_autoload自动加载机制的深入理解

    PHP autoload与spl_autoload自动加载机制的深入理解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-05我要评论 本篇文章是对PHP中的autoload与spl_ ...

  6. 《Linux内核与分析》第六周

    20135130王川东 1.操作系统的三大管理功能包括:进程管理,内存管理,文件系统. 2. Linux内核通过唯一的进程标识PID来区别每个进程.为了管理进程,内核必须对每个进程进行清晰的描述,进程 ...

  7. 20162328蔡文琛 Java课程总结

    20162328 2016-2017-2<程序设计与数据结构>课程总结 一.每周作业.结对编程博客的链接汇总 预备作业01 20162328:表达对专业的期许.浅谈师生关系.对未来学习任务 ...

  8. Python:字符串中引用外部变量的3种方法

    方法一: username=input('username:') age=input('age:') job=input('job:') salary=input('salary') info1='' ...

  9. SDN练习一

    SDN练习第一题 题目描述 实现网络拓扑: 具体要求: 南向接口采用OpenFlow 协议. 可查看网络的拓扑信息视图. H1.H2.H3.H4 任意两两可互通. 实现思路 利用mininet可视化图 ...

  10. 青岛 2016ICPC 区域现场赛题目

    A. Relic Discovery B. Pocket Cube C. Pocky D. Lucky Coins E. Fibonacci F. Lambda Calculus G. Coding ...