博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【NOI 2018】归程(Kruskal重构树)
阅读量:4306 次
发布时间:2019-06-06

本文共 3760 字,大约阅读时间需要 12 分钟。

题面在这里就不放了。

同步赛在做这个题的时候,心里有点纠结,很容易想到离线的做法,将边和询问一起按水位线排序,模拟水位下降,维护当前的各个联通块中距离$1$最近的距离,每次遇到询问时输出所在联通块的信息。

离线的思路对满分做法有一定的启发性,很容易想到将并查集持久化一下就能支持在线了。

但是这个是两个$log$的,有卡常的风险也不是很方便写。

当时思考了一下就快速写完离线做法就去做其他题了。

对于这道题,有一个更好的做法:Kruskal重构树。

事实上如果你了解这个东西,那你就能很快的给出解,那仅此以这道题作为学习Kruskal重构树的例子。

先给出一个经典的模型:

  • 给定一张无向图,每次询问两点之间所有简单路径中最大边权的最小值。

一个常规的做法就是建出最小生成树,答案就是树上路径的最大边权,正确性显然。

当然也可以用我们要讲的Kruskal重构树来解决,算法虽不同,思想类似。

Kruskal中我们连接两个联通块(子树)时直接用一条边将对应的两个点相连,但在Kruskal重构树中,我们先建一个虚点作为两个子树的树上父亲,让两个联通块分别与该点相连,注意的是要维护并查集合并时的有序性。

我们称新建的虚点为方点,代表了原图中的一条边,原图中的点为圆点,则该树有一些优雅的性质:

  1. 这是一颗二叉树,并且相当于一个堆,因为边是有顺序合并的。
  2. 最小生成树上路径的边权信息转化成了点权信息。

那么回顾刚刚的那个模型,每个询问就相当于回答Kruskal重构树上两点$lca$的权值。

 

最后在回来看这道题,就显得十分轻松了。

我们把每条边按照水位线从高到低依次插入,可以发现每次规定一个水位下限,车子能走的范围对应了Kruskal重构树上的一棵子树,那么每次只要倍增找到最紧的那个限制的点就可以了。

1 #include 
2 #include
3 #include
4 #include
5 6 typedef long long LL; 7 const int N = 600005, INF = 2e9 + 7, LOG = 21; 8 9 int tc, n, m, Qi, k, s; 10 int dis[N], flg[N], val[N], mdi[N], gr[LOG][N]; 11 std::priority_queue
> Q; 12 13 inline void Read(int &x) { 14 x = 0; static char c; 15 for (c = getchar(); c < '0' || c > '9'; c = getchar()); 16 for (; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar()); 17 } 18 19 struct Edge { 20 int u, v, a; 21 inline friend bool operator < (Edge a, Edge b) { 22 return a.a > b.a; 23 } 24 } e[N]; 25 26 int yun, las[N], to[N << 1], pre[N << 1], wi[N << 1]; 27 inline void Add(int a, int b, int c = 0) { 28 to[++yun] = b; wi[yun] = c; pre[yun] = las[a]; las[a] = yun; 29 } 30 void Gragh_clear() { 31 memset(gr, 0, sizeof gr); 32 memset(las, 0, sizeof las); 33 yun = 0; 34 } 35 36 namespace DSU { 37 int fa[N]; 38 void Init() { 39 for (int i = 1; i <= n + m; ++i) { 40 fa[i] = i; 41 if (i <= n) mdi[i] = dis[i], val[i] = -1; 42 } 43 } 44 int Seek(int x) { 45 return (x == fa[x])? (x) : (fa[x] = Seek(fa[x])); 46 } 47 void Merge(int x, int y) { 48 fa[Seek(y)] = x; 49 } 50 } 51 52 void Dij() { 53 for (int i = 1; i <= n; ++i) { 54 dis[i] = INF; flg[i] = 0; 55 } 56 dis[1] = 0; 57 Q.push(std::make_pair(0, 1)); 58 for (; !Q.empty(); ) { 59 int x = Q.top().second; Q.pop(); 60 if (flg[x]) continue; 61 flg[x] = 1; 62 for (int i = las[x]; i; i = pre[i]) { 63 if (dis[to[i]] > dis[x] + wi[i]) { 64 dis[to[i]] = dis[x] + wi[i]; 65 Q.push(std::make_pair(-dis[to[i]], to[i])); 66 } 67 } 68 } 69 } 70 71 int main() { 72 freopen("return.in", "r", stdin); 73 freopen("return.out", "w", stdout); 74 75 scanf("%d", &tc); 76 for (; tc; --tc) { 77 scanf("%d%d", &n, &m); 78 Gragh_clear(); 79 for (int i = 1, x, y, a, l; i <= m; ++i) { 80 //scanf("%d%d%d%d", &x, &y, &l, &a); 81 Read(x); Read(y); Read(l); Read(a); 82 Add(x, y, l); Add(y, x, l); 83 e[i] = (Edge) { x, y, a }; 84 } 85 Dij(); DSU::Init(); 86 87 std::sort(e + 1, e + 1 + m); 88 for (int i = 1; i <= m; ++i) { 89 int x = DSU::Seek(e[i].u), y = DSU::Seek(e[i].v); 90 if (x == y) continue; 91 val[i + n] = e[i].a; 92 mdi[i + n] = std::min(mdi[x], mdi[y]); 93 DSU::Merge(i + n, x); DSU::Merge(i + n, y); 94 gr[0][x] = gr[0][y] = i + n; 95 } 96 97 for (int i = 1; i < LOG; ++i) { 98 for (int j = 1; j <= n + m; ++j) { 99 if (gr[i - 1][j]) gr[i][j] = gr[i - 1][gr[i - 1][j]];100 }101 }102 103 scanf("%d%d%d", &Qi, &k, &s);104 for (int x, a, lans = 0; Qi; --Qi) {105 //scanf("%d%d", &x, &a);106 Read(x); Read(a);107 x = (x + (LL) k * lans - 1) % n + 1;108 a = (a + (LL) k * lans) % (s + 1);109 for (int i = LOG - 1; ~i; --i) {110 if (gr[i][x] && val[gr[i][x]] > a) x = gr[i][x];111 }112 printf("%d\n", mdi[x]);113 lans = mdi[x];114 }115 }116 117 return 0;118 }
View Code

 

$\bigodot$技巧&套路:

  • kruskal重构树的构建,最小生成树上最值问题的再探。

转载于:https://www.cnblogs.com/Dance-Of-Faith/p/9333015.html

你可能感兴趣的文章
HTTP的幂等性
查看>>
十三.Java使用Protobuf3
查看>>
zabbix监控mysql
查看>>
Zabbix监控win10系统
查看>>
贴两个mysql优化的配置文件
查看>>
grafana 的配置文件,和使用mysql数据库做持久化
查看>>
pve 导入 ova
查看>>
grafana+mysql忘记admin密码解决方法
查看>>
常用命令备忘 lsof
查看>>
使用内存来优化磁盘缓存的读写速度
查看>>
命令备忘 ss
查看>>
使用docker安装wazuh
查看>>
linux 常用性能优化
查看>>
安装wazuh-agent
查看>>
修改windows网络参数,让上网更快
查看>>
利用nc当作备用shell管理方案.
查看>>
备用shell管理方案之butterfly+nginx+https
查看>>
使用开源软件 jumpserver 搭造自己的堡垒机
查看>>
[报错解决] "MySQL server has gone away" 解决思路
查看>>
http状态码-备查
查看>>