<更新提示>

<第一次更新>

<第二次更新>新增一道例题及讲解


<正文>

Exkruscal

\(Exkruscal\)又称\(Kruscal\)重构树,是一种利用经典算法\(Kruscal\)来实现的构造算法,可以将一张无向图重构为一棵具有\(2n-1\)个节点的树,这棵树具有许多特殊的性质,可以用来解决许多问题。

那么我们来了解一下这个新算法。

Kruscal

先回顾一下\(Kruscal\)算法吧。这是一个经典的图论算法,用于求解无向图的最小生成树。

其算法大致思路为将边用边表的形式储存,按照权值从小到大排序,然后枚举每一条边,尝试连接不在同一联通块中的两个节点,这一过程用并查集来维护。

重构树

那么我们怎么用\(Kruscal\)算法来重构树呢?我们还是先将边按边权从小到大排序,然后按照\(Kruscal\)算法的流程来,每一次加入一条连接两个不同联通块的点,并将这一过程用并查集来维护。

不同的是,对于每一条\(Kruscal\)算法选中的边,我们要新建一个节点,将该点的点权赋值为选中边的边权,并将该点连向选中边连接的两个点。

这样,对于\(Kruscal\)算法选中的\(n-1\)条边,我们就构造出了一个具有\(2n-1\)个节点的树,其中,树的\(n\)个叶节点对应原图的\(n\)个节点它们没有点权,而其他的所有点均有一个点权。

性质

我们已经了解如何用\(Kruscal\)算法重构树了,那么,这棵树具有如下的性质,可以帮助我们解题。

\(1.\)这棵树具有堆性质(大根堆)

\(2.\)原图两点之间路径的最大边权最小值是重构树上两点\(lca\)的权值

\(3.\)树中代表原图中的点的节点全是叶子节点,其余节点都代表了一条边的边权

\(Code:\)

  1. inline void Exkruscal(void)
  2. {
  3. sort(l+1,l+m+1,compare);//l是边表
  4. for(int i=1;i<=2*n;i++)
  5. fa[i]=i;//并查集初始化
  6. for(int i=1;i<=m;i++)
  7. {
  8. int x=find(l[i].x),y=find(l[i].y);
  9. if(x==y)continue;
  10. fa[x]=fa[y]=++n;
  11. val[n]=l[i].val;//点权
  12. insert(n,x);//连边
  13. insert(n,y);
  14. }
  15. }

Network(BZOJ3732)

Description

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。

图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).

现在有 K个询问 (1 < = K < = 15,000)。 每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Input Format

第一行: N, M, K。

第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。

第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Output Format

对每个询问,输出最长的边最小值是多少。

Sample Input

  1. 6 6 8
  2. 1 2 5
  3. 2 3 4
  4. 3 4 3
  5. 1 4 8
  6. 2 5 7
  7. 4 6 2
  8. 1 2
  9. 1 3
  10. 1 4
  11. 2 3
  12. 2 4
  13. 5 1
  14. 6 2
  15. 6 1

Sample Output

  1. 5
  2. 5
  3. 5
  4. 4
  5. 4
  6. 7
  7. 4
  8. 5

解析

\(Solution\ 1\)

由于最小生成树一定是瓶颈生成树,所以我们可以将原图求最小生成树直接重新建图,保证两点之间路径的最大权值最小。然后树上倍增\(LCA\)顺便求最大值即可。

\(Solution\ 2\)

\(Exkruscal\)模板题,直接\(Kruscal\)重构树,然后求\(LCA\)点权即可。

\(Code:\)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define mset(name,val) memset(name,val,sizeof name)
  4. #define filein(str) freopen(str".in","r",stdin)
  5. #define fileout(str) freopen(str".out","w",stdout)
  6. const int N=15020,M=30020,K=20020,MaxlogN=25;
  7. struct Link{int x,y,val;}l[M];
  8. struct edge{int ver,next;}e[2*N];
  9. int n,m,q,t,fa[2*N],val[2*N],depth[2*N],f[2*N][MaxlogN],Last[2*N];
  10. inline void input(void)
  11. {
  12. scanf("%d%d%d",&n,&m,&q);
  13. for(int i=1;i<=m;i++)
  14. scanf("%d%d%d",&l[i].x,&l[i].y,&l[i].val);
  15. }
  16. inline bool compare(Link a,Link b)
  17. {
  18. return a.val<b.val;
  19. }
  20. inline void insert(int x,int y)
  21. {
  22. e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
  23. }
  24. inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
  25. inline void Exkruscal(void)
  26. {
  27. sort(l+1,l+m+1,compare);
  28. for(int i=1;i<=2*n;i++)
  29. fa[i]=i;
  30. for(int i=1;i<=m;i++)
  31. {
  32. int x=find(l[i].x),y=find(l[i].y);
  33. if(x==y)continue;
  34. fa[x]=fa[y]=++n;
  35. val[n]=l[i].val;
  36. insert(n,x);
  37. insert(n,y);
  38. }
  39. }
  40. inline void dfs(int x,int dep)
  41. {
  42. depth[x]=dep;
  43. for(int i=Last[x];i;i=e[i].next)
  44. {
  45. int y=e[i].ver;
  46. if(f[x][0]==y)continue;
  47. f[y][0]=x;
  48. dfs(y,dep+1);
  49. }
  50. }
  51. inline void dp(void)
  52. {
  53. f[n][0]=-1;
  54. for(int k=1;(1<<k)<n;k++)
  55. for(int i=1;i<=n;i++)
  56. if(f[i][k-1]<0)f[i][k]=-1;
  57. else f[i][k]=f[f[i][k-1]][k-1];
  58. }
  59. inline int LCA(int x,int y)
  60. {
  61. if(depth[x]>depth[y])
  62. swap(x,y);
  63. for(int d=depth[y]-depth[x],i=0;d;d>>=1,i++)
  64. if(1&d)y=f[y][i];
  65. if(x==y)return x;
  66. for(int i=MaxlogN-1;i>=0;i--)
  67. {
  68. if(f[x][i]^f[y][i])
  69. {
  70. x=f[x][i];
  71. y=f[y][i];
  72. }
  73. }
  74. return f[x][0];
  75. }
  76. inline void solve(void)
  77. {
  78. for(int i=1;i<=q;i++)
  79. {
  80. int a,b;
  81. scanf("%d%d",&a,&b);
  82. printf("%d\n",val[LCA(a,b)]);
  83. }
  84. }
  85. int main(void)
  86. {
  87. input();
  88. Exkruscal();
  89. dfs(n,1);
  90. dp();
  91. solve();
  92. return 0;
  93. }

路径权值

Description

给定一个带权树,树上任意两点间的路径权值d(x,y)定义为x,y这两个点之间路径上的最小值,树上任意一点x的权值定义为这个点到树上其他所有点的路径权值和,即∑d(x,i),现求树上每一个点的路径权值和。

Input Format

首先输入一个整数 n(1≤n≤100000),表示树的点的个数。 

接下来n−1行,每行三个整数 x,y,s(1≤x,y≤n,1≤s≤1000),表示编号为x的节点和编号为y的节点之间存在一条权值为s的边,树上每个点的编号为1~n

Output Format

n行,每行输出每个节点对应的路径权值和

Sample Input

  1. 4
  2. 1 2 2
  3. 2 4 1
  4. 2 3 1

Sample Output

  1. 4
  2. 4
  3. 3
  4. 3

解析

看到两点路径间的最小值就想到是可以用\(Exkruscal\)的。

先将边按边权从大到小排序,重构树一下,两点\(LCA\)的权值就是树上两点路径的最小值。对于重构树中的每一个非叶子节点\(x\),它的左子树中的每一个叶节点到右子树中的每一个叶节点的真实路径中必然经过\(x\)所代表的边。

我们设\(p_i\)代表原树中节点\(i\)的路径权值和,那么对于刚才提到的一个非叶子节点\(x\),\(x\)的左子树中所有叶子结点的\(p\)值都应该加上\(x\)右子树大小乘\(val_x\),右子树也是同理(加上\(x\)左子树大小乘\(val_x\))。

最后,我们要输出每个节点的\(p\)值。这样的话,问题就转换成了我们需要一个数据结构,能够实现区间加法和单点查询,可以用树状数组配合差分实现。

还有一个问题,如果做区间加法时,子树中叶节点所对应的编号不连续怎么办,我们可以前序遍历给每一个叶子节点重新标一个编号,用于树状数组,最后输出时输出每个节点对应编号的值即可。

\(Code:\)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=100000+20;
  4. int dif[N],n,n_,Last[N*2],t,f[N*2],val[N*2],size[N*2],cnt;
  5. struct LINK{int x,y,val;}l[N];
  6. struct EDGE{int ver,next;}e[N*2];
  7. struct TREE{int l,r;}tree[N*2];
  8. inline bool compare(LINK a,LINK b)
  9. {
  10. return a.val>b.val;
  11. }
  12. inline int find(int x)
  13. {
  14. return f[x]==x?x:f[x]=find(f[x]);
  15. }
  16. inline void insert(int x,int y)
  17. {
  18. e[++t].ver=y;e[t].next=Last[x];Last[x]=t;
  19. }
  20. inline int lowbit(int x){return x&(-x);}
  21. inline void modify(int pos,int val)
  22. {
  23. for(;pos<=n_;pos+=lowbit(pos))dif[pos]+=val;
  24. }
  25. inline int query(int pos)
  26. {
  27. int res=0;
  28. for(;pos;pos-=lowbit(pos))res+=dif[pos];
  29. return res;
  30. }
  31. inline void input(void)
  32. {
  33. scanf("%d",&n);
  34. for(int i=1;i<n;i++)
  35. scanf("%d%d%d",&l[i].x,&l[i].y,&l[i].val);
  36. n_=n;
  37. }
  38. inline void Exkruscal(void)
  39. {
  40. sort(l+1,l+n,compare);
  41. for(int i=1;i<=2*n-1;i++)
  42. f[i]=i;
  43. for(int i=1;i<n;i++)
  44. {
  45. int x=find(l[i].x),y=find(l[i].y);
  46. f[x]=f[y]=++n_;
  47. val[n_]=l[i].val;
  48. insert(n_,x);insert(n_,y);
  49. }
  50. }
  51. inline void dfs(int x)
  52. {
  53. size[x]=(x<=n);
  54. tree[x].l=++cnt;
  55. for(int i=Last[x];i;i=e[i].next)
  56. {
  57. int y=e[i].ver;
  58. dfs(y);
  59. size[x]+=size[y];
  60. }
  61. tree[x].r=cnt;
  62. }
  63. inline void solve(void)
  64. {
  65. for(int i=n+1;i<=n_;i++)
  66. {
  67. int lson=0,rson=0;
  68. for(int j=Last[i];j;j=e[j].next)
  69. {
  70. if(lson)rson=e[j].ver;
  71. else lson=e[j].ver;
  72. }
  73. modify(tree[lson].l,size[rson]*val[i]);
  74. modify(tree[lson].r+1,-size[rson]*val[i]);
  75. modify(tree[rson].l,size[lson]*val[i]);
  76. modify(tree[rson].r+1,-size[lson]*val[i]);
  77. }
  78. }
  79. int main(void)
  80. {
  81. input();
  82. Exkruscal();
  83. dfs(n_);
  84. solve();
  85. for(int i=1;i<=n;i++)
  86. printf("%d\n",query(tree[i].l));
  87. return 0;
  88. }

<后记>

『Kruscal重构树 Exkruscal』的更多相关文章

  1. loj2876 水壶 [JOISC 2014 Day2] kruscal重构树

    正解:kruscal重构树+bfs 解题报告: 我永远喜欢loj! 感觉这题和这题挺像的,,,预处理和解题方法都是,,,所以大概整体二分能过去? 但因为做这题主要是入门一下kruscal重构树,,,所 ...

  2. kruscal重构树略解

    我们先看一道题:Luogu P4197 Peaks 这道题珂以用启发式合并+主席树来做 那么强制在线呢?(bzoj 3551 [ONTAK2010]Peaks加强版) 离线做法就不行了 我们就要用一个 ...

  3. BZOJ_3545_[ONTAK2010]Peaks_主席树+倍增+kruscal重构树+dfs序

    BZOJ_3545_[ONTAK2010]Peaks_主席树+倍增+kruscal重构树 Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道 ...

  4. [NOI2018]归程(kruscal重构树)

    [NOI2018]归程 题面太长辣,戳这里 模拟赛上写了一个spfa (关于spfa,它已经死了),然后一个st表水完暴力跑路.考后说是Kruscal重构树或者可持久化并查集???这都是些什么东西.不 ...

  5. $ CometOJ-Contest\#11\ D$ $Kruscal$重构树

    正解:$Kruscal$重构树 解题报告: 传送门$QwQ$ 发现一个图上搞就很麻烦,考虑变为生成树达到原有效果. 因为在询问的时候是要求走到的点编号尽量小,发现这个时候点的编号就成为限制了,于是不难 ...

  6. 洛谷$P4768\ [NOI2018]$归程 $kruscal$重构树

    正解:$kruscal$重构树 解题报告: 传送门$QwQ$ 语文不好选手没有人权$TT$连题目都看不懂真的要哭了$kk$ 所以先放个题目大意?就说给定一个$n$个点,$m$条边的图,每条边有长度和海 ...

  7. 学习笔记:Kruscal 重构树

    网上感觉没有什么很详细 + 证明的讲解啊) 前置:Kruskal 求最小生成树. 这个算法可以将一棵树 / 无向连通图重构成一颗有性质的新树. 算法可以解决一些树上瓶颈边权之类的问题,可以把需要持久化 ...

  8. 【题解】洛谷P1967 [NOIP2013TG] 货车运输(LCA+kruscal重构树)

    洛谷P1967:https://www.luogu.org/problemnew/show/P1967 思路 感觉2013年D1T3并不是非常难 但是蒟蒻还是WA了一次 从题目描述中看出每个点之间有许 ...

  9. BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增+最短路

    BZOJ_5415_[Noi2018]归程_kruscal重构树+倍增 Description www.lydsy.com/JudgeOnline/upload/noi2018day1.pdf 好久不 ...

随机推荐

  1. Json的学习

    json的简介 Json是项目中常用的一种,数据格式简单,易于读写,格式都是压缩的,占用带宽小,轻量级,支持多种语言,可以直接为服务器代码使用. json常用支持的转化,(map集合,字符串,还有对象 ...

  2. less 命令翻页键

    less 是linux快速浏览文件的命令(防止 误修改文件)  less主要就是 浏览文件 查找文件 浏览文件涉及到的就是上下翻页 具体翻页的按键如下表 less 向上翻页 向下翻页 一页 b (ba ...

  3. Django合集

    Django基础 Django--简介 Django--web框架简介 浅析uWSGI.uwsgi.wsgi Django--url(路由)配置 Django--模板层 Django--视图层 Dja ...

  4. mac效率工具

    前言:在命令行中切换目录是最常用的操作,我相信一遍又一遍重复“cd ls cd ls cd ls ……”绝对会让你抓狂. 记录一下,方便下次系统重装,哈哈 一. oh-my-zsh mac 预装了 z ...

  5. VS2017下使用Git遇到的问题

    我在使用最新版的VS2017时,想获取服务器上最新代码,Fetch到了最新修改,但是在拉取代码的时候出现了问题 user@user-PC MINGW64 /d/demo/myrepos (dev-cs ...

  6. java中的异常类

    Java中的异常: 1. Throwable是所有异常的根,java.lang.Throwable Throwable包含了错误(Error)和异常(Exception),Exception又包含了运 ...

  7. SpringBoot报错:Invalid bound statement (not found)

    错误原因: 没有发现Mybatis配置文件的路径 解决方法: 检查Mapper包名与xml文件标签的namespace数据名称是否相同 <mapper namespace="com.t ...

  8. 转 c#性能优化秘密

    原文:http://www.dotnetperls.com/optimization Generally, using the simplest features of the language pr ...

  9. 发现一款适合php网站的管理软件——kodexplorer,能取代ftp

    今天偶然看到可以利用可道云来管理网站的文件.可道云不需要数据库,因此搭建非常简单.搭建的方法也很简单.传统的 WordPress 站点的文件管理,通常是是通过 FTP 或者服务器面板自带的文件管理器来 ...

  10. vim 常用指令

    其他命令 <c-L> 重绘屏幕 <c-z> 挂起vim回到shell,想继续vim只需要输入 fg <c-x-f> 文件路径提示 <c-N> 当前文件中 ...