\(kruskal\) 重构树学习笔记

前言

\(8102IONCC\) 中考到了,本蒟蒻不会,所以学一下。

前置知识

\(kruskal​\) 求最小(大)生成树,树上求 \(lca​\)。

算法详解

\(kruskal\) 重构树可以解决瓶颈路问题(如:\(noip2013\) \(d1t3\) 货车运输,可以当做模板题来做,本文中也将此题作为例题);

我们来思考一下 \(kruskal\) 求最小(大)生成树的过程(后文中以最大生成树为例),大致过程可以概述为:将图中所有边从大到小排序,枚举,如果该边左右两端的点不在同一个联通块里就连起来,即:该边在最大生成树上。(其中联通块用 \(dsu\) 维护)

\(kruskal\) 重构树就是把最大生成树上的边建成树,实现过程:在 \(kruskal\) 算法进行过程中,对于每次要连接的两个联通块 \(A\) 和 \(B\),用一个新的节点当做 \(A\) \(B\) 在重构树中的父亲节点,并把新节点的点权设为连接联通块的边的边权,以此操作来代替连边操作,联通块同样用 \(dsu\) ,只不过是把 \(A\) \(B\) 并入新节点。

有一个比较明显的性质:最后建成的树一定是一个堆(大根堆小根堆视情况而定),因为边是按大小顺序枚举的,如果我表达不太清楚 \((QAQ)\) ,可以配合图解来学习。

具体来说,如果 \(kruskal\) 求最大生成树的过程是这样的:

那么建树过程大概就是这样的(蓝色是新建节点):

可以很直观地看到这真的是个堆,上文已经提到,不再赘述。

\(u,v\) 之间的瓶颈路,即:\(u,v\) 在重构树中的 \(lca\) 的点权。因为建树过程和 \(kruskal\) 同步进行,所以每个新建节点的点权是可以连接两棵子树所在联通块的最大边权,同时因为堆的性质 \(lca\) 是整颗以 \(lca\) 为根的子树中的最小值。(本人语文水平不及格,可以结合图片和 \(kruskal\) 的过程来理解)

\(p.s.\) 同样可以利用该性质证明瓶颈路一定在最大生成树上。

例题

\((noip2013\) \(d1t3)\)

这是一道模板题。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
inline int in() {
int x=0;char c=getchar();bool f=false;
while(c<'0'||c>'9') f|=c=='-', c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48), c=getchar();
return f?-x:x;
} const int N = 1e5+5, M = 5e5+5;
struct info {
int l, r, w;
inline bool operator < (const info &x) const {
return this->w > x.w;
}
}a[M+N]; int n, m, tot, fa[N<<1]; namespace kruskal_tree {
struct edge {
int next, to;
}e[N];
int head[N<<1], val[N<<1], cnt=1, size[N<<1], hson[N<<1], fro[N<<1], dep[N<<1];
inline void jb(int u, int v) {
e[++cnt]=(edge){head[u], v};
head[u]=cnt;
}
void dfs_h(int u) {
size[u]=1;
for(int i=head[u];i;i=e[i].next) {
int v=e[i].to;
fa[v]=u, size[u]+=size[v], dep[v]=dep[u]+1;
if(size[v]>size[hson[u]]) hson[u]=v;
dfs_h(v);
}
}
void dfs_f(int u, int father) {
fro[u]=father;
if(hson[u]) dfs_f(hson[u], father);
for(int i=head[u];i;i=e[i].next)
if(e[i].to!=hson[u]) dfs_f(e[i].to, e[i].to);
}
inline int lca(int u, int v) {
while(fro[u]!=fro[v]) {
if(dep[fro[u]]>dep[fro[v]]) std::swap(u, v);
v=fa[fro[v]];
}
return dep[u]<dep[v]?u:v;
}
inline void pre() {
dep[tot]=1;
dfs_h(tot); dfs_f(tot, tot);
}
inline int calc(int u, int v) {
return val[lca(u, v)];
}
} int get_fa(int u) {
return fa[u]==u?u:fa[u]=get_fa(fa[u]);
} inline void kruskal(info *e) {
tot=n;
std::sort(e+1, e+1+m);
for(int i=1;i<=n<<1;++i) fa[i]=i;
for(int i=1;i<=m;++i) {
int fx=get_fa(e[i].l), fy=get_fa(e[i].r);
if(fx==fy) continue;
fa[fx]=fa[fy]=++tot, kruskal_tree::val[tot]=e[i].w;
kruskal_tree::jb(tot, fx), kruskal_tree::jb(tot, fy);
}
kruskal_tree::pre();
} int main() {
n=in(), m=in();
for(int i=1;i<=m;++i)
a[i].l=in(), a[i].r=in(), a[i].w=in();
for(int i=1;i<=n;++i)
a[++m]=(info){0, i, -1}; kruskal(a); int q=in();
while(q--) {
int u=in(), v=in();
printf("%d\n", kruskal_tree::calc(u, v));
}
return 0;
}

\((noi2018\) \(d1t1)\)

一句话题解: \(dijsktra\) 预处理 \(1\) 为起点的最短路(权值为长度);建好 \(kruskal\) 重构树(海拔为权值),每次查询 \(v\) 时树上倍增一直跳就好了 \(qwq\)。

时间复杂度:\(\Theta \Big(T \times \big (mlogm+(q+n)logn \big) \Big)\)

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
typedef long long ll;
inline int in() {
int x=0;char c=getchar();bool f=false;
while(c<'0'||c>'9') f|=c=='-', c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48), c=getchar();
return f?-x:x;
} const int N = 2e5+5, M = 4e5+5; struct info {
int l, r, w;
friend inline bool operator < (const info &x, const info &y) {
return x.w > y.w;
}
}a[M];
struct edge {
int next, to, w;
}e[M<<1];
int head[N], cnt, n, m, tot, fa[M], d[N];
bool vis[N]; template<typename T>
inline void chk_min(T &_, T __) { _=_<__?_:__; } namespace kt {
struct edge {
int next, to;
}e[N<<1];
int cnt=1, head[N<<1], min[N<<1], dep[N<<1], fa[19][N<<1], a[N<<1], L[19];
inline void jb(int u, int v) {
e[++cnt]=(edge){head[u], v};
head[u]=cnt;
}
void dfs(int u) {
for(int i=1;i<=18;++i)
if(dep[u]>L[i]) fa[i][u]=fa[i-1][fa[i-1][u]];
else break; min[u]=2147483647;
if(1<=u&&u<=n) min[u]=d[u], a[u]=2147483647;
for(int i=head[u];i;i=e[i].next) {
int v=e[i].to;
dep[v]=dep[u]+1, fa[0][v]=u;
dfs(v);
chk_min(min[u], min[v]);
}
}
inline int query(int u, int s) {
for(int i=18;i>=0;--i)
if(dep[u]>L[i]&&a[fa[i][u]]>s)
u=fa[i][u];
return min[u];
}
} inline void add_edge(int u, int v, int w) {
e[++cnt]=(edge){head[u], v, w}, head[u]=cnt;
e[++cnt]=(edge){head[v], u, w}, head[v]=cnt;
} typedef std::pair <int, int> pii;
inline void dijkstra() {
std::priority_queue <pii> q;
memset(d, -1, sizeof(d));
memset(vis, false, sizeof(vis));
d[1]=0;
q.push(pii(0, 1));
while(!q.empty()) {
int u=q.top().second; q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].next) {
int v=e[i].to;
if(d[u]+e[i].w<d[v]||d[v]==-1)
d[v]=d[u]+e[i].w, q.push(pii(-d[v], v));
}
}
} int get_fa(int u) {
return fa[u]==u?u:fa[u]=get_fa(fa[u]);
} inline void kruskal(info *e) {
tot=n;
for(int i=1;i<=n<<1;++i) fa[i]=i;
std::sort(e+1, e+1+m);
for(int i=1;i<=m;++i) {
int fx=get_fa(e[i].l), fy=get_fa(e[i].r);
if(fx==fy) continue;
++tot, kt::a[tot]=e[i].w;
kt::jb(tot, fx), kt::jb(tot, fy);
fa[fx]=fa[fy]=tot;
}
} inline void init() {
cnt=kt::cnt=1;
memset(head, 0, sizeof(head));
memset(kt::head, 0, sizeof(kt::head));
} int main() {
int T=in();
kt::L[0]=1;
for(int i=1;i<=18;++i) kt::L[i]=kt::L[i-1]<<1;
while(T--) {
init();
n=in(), m=in();
for(int i=1, u, v, l, h;i<=m;++i) {
u=in(), v=in(), l=in(), h=in();
a[i]=(info){u, v, h};
add_edge(u, v, l);
} dijkstra();
kruskal(a); kt::dep[tot]=1;
kt::dfs(tot);
int q=in(), k=in(), s=in(), ans=0, v, a;
while(q--) {
int v=(in()+(ll)k*ans-1)%n+1, a=(in()+(ll)k*ans)%(s+1);
ans=kt::query(v, a);
printf("%d\n", ans);
}
}
return 0;
}

kruskal重构树学习笔记的更多相关文章

  1. Kruskal重构树学习笔记+BZOJ3732 Network

    今天学了Kruskal重构树,似乎很有意思的样子~ 先看题面: BZOJ 题目大意:$n$ 个点 $m$ 条无向边的图,$k$ 个询问,每次询问从 $u$ 到 $v$ 的所有路径中,最长的边的最小值. ...

  2. P4197 Peaks [克鲁斯卡尔重构树 + 主席树][克鲁斯卡尔重构树学习笔记]

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

  3. 洛谷P4197 Peaks&&克鲁斯卡尔重构树学习笔记(克鲁斯卡尔重构树+主席树)

    传送门 据说离线做法是主席树上树+启发式合并(然而我并不会) 据说bzoj上有强制在线版本只能用克鲁斯卡尔重构树,那就好好讲一下好了 这里先感谢LadyLex大佬的博客->这里 克鲁斯卡尔重构树 ...

  4. [算法模板]Kruskal重构树

    [算法模板]Kruskal重构树 kruskal重构树是一个很常用的图论算法.主要用于解决u->v所有路径上最长边的最小值,就是找到\(u->v\)的一条路径,使路径上的最长边最小. 图片 ...

  5. 【学习笔记】Kruskal 重构树

    1. 例题引入:BZOJ3551 用一道例题引入:BZOJ3551 题目大意:有 \(N\) 座山峰,每座山峰有他的高度 \(h_i\).有些山峰之间有双向道路相连,共 \(M\) 条路径,每条路径有 ...

  6. [学习笔记]kruskal重构树 && 并查集重构树

    Kruskal 重构树 [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树 kruskal是一个性质优秀的算法 加入的边是越来越劣的 科学家们借这个特点尝试搞一点事 ...

  7. 算法学习——kruskal重构树

    kruskal重构树是一个比较冷门的数据结构. 其实可以看做一种最小生成树的表现形式. 在普通的kruskal中,如果一条边连接了在2个不同集合中的点的话,我们将合并这2个点所在集合. 而在krusk ...

  8. [luogu P4197] Peaks 解题报告(在线:kruskal重构树+主席树 离线:主席树+线段树合并)

    题目链接: https://www.luogu.org/problemnew/show/P4197 题目: 在Bytemountains有N座山峰,每座山峰有他的高度$h_i$.有些山峰之间有双向道路 ...

  9. Kruskal重构树——[NOI2018] 归程

    题目链接: UOJ LOJ 感觉 Kruskal 重构树比较简单,就不单独开学习笔记了. Statement 给定一个 \(n\) 点 \(m\) 边的无向连通图,用 \(l,a\) 描述一条边的长度 ...

随机推荐

  1. pycharm中运行时添加配置 及pytest模式怎么修改为run模式

    会发现不是控制台输出,而是pytest模式. 修改: 当运行时,发现无法运行: 然后点击Add Configuration, 点击加号,点击Python: 选择脚本路径和解释器.点击OK即可.

  2. 浅析RPC远程过程调用基本原理

    在校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互联网公司就会发现,公司的系 ...

  3. 验证码的实现py3

    import randomflag = 1try_=0while (flag): try_ +=1 yan = "" for i in range(0,4): cun=random ...

  4. Linux centos nginx下载安装初步

    下载源码包解压编译 1.下载 # wget http://nginx.org/download/nginx-1.9.9.tar.gz 2.解压 # tar xvf nginx-1.9.9.tar.gz ...

  5. springboot 的部分细节

    Application.properties 中#指定端口号 server.port= #指定访问路径必须以/crud/xxx 开始 server.servlet.context-path=/crud ...

  6. pc端常規頁面實現

    https://www.cnblogs.com/adc8868/p/5996885.html https://blog.csdn.net/chose_DoIt/article/details/8042 ...

  7. django xadmin(1)

    filter_horizontal 从‘多选框’的形式改变为‘过滤器’的方式,水平排列过滤器,必须是一个 ManyToManyField类型,且不能用于 ForeignKey字段,默认地,管理工具使用 ...

  8. CF1153 F. Serval and Bonus Problem(dp)

    题意 一个长为 \(l\) 的线段,每次等概率选择线段上两个点,共选出 \(n\) 条线段,求至少被 \(k\) 条线段覆盖的长度期望. 数据范围 \(1 \le k \le n \le 2000, ...

  9. 【学习笔记】JS知识点整理

    1 原型/原型链 1-1 原型 定义:原型是function对象的一个属性,定义了构造函数制造出的对象的公共祖先.通过该构造函数产生的对象,可以继承该原型的属性和方法. 原型是一个对象. 可以利用原型 ...

  10. ElasticSearch常用操作

    查看某个INDEX库某个TYPE表,某个字段的分词结果  GET /${index}/${type}/${id}/_termvectors?fields=${fields_name}http://19 ...