题意

给你一个无向图,其中每条边有两个值 \(l, a\) 代表一条边的长度和海拔。

其中有 \(q\) 次询问(强制在线),每次询问给你两个参数 \(v, p\) ,表示在 \(v\) 出发,能开车经过海拔 \(> p\) 的边,其中 \(\le p\) 的边只能步行,步行后不能继续开车了。

询问它到 \(1\) 号点最少要步行多远。

多组数据。\(n \le 200000~~ m,q \le 400000\) 。

题解

一个直观的想法,对于每次询问,我们保留 \(>p\) 的边,然后求出联通块。

求出它所在联通块到 \(1\) 距离最小的那个点,就是这次询问的答案。

到 \(1\) 距离的就是把 \(1\) 当做起点跑一遍单源最短路就行了,注意要用 \(Dijkstra\) ,\(Spfa\) 可以被卡掉。

复杂度就是 \(O((n + m) \log n)\) 的。

这下我们只需要询问每个点所在联通块的最小值就行了。

不难想到,把边按海拔从大到小加入,然后用并查集维护联通块最小值。

这样的话就可以离线实现这个过程了。

由于强制在线,我们可以用 可持久化并查集 实现这个过程。但这样其实不好写,常数其实还有一点大。

我就介绍原题正解的做法,也就是 Kruskal重构树

\(Kruskal\) 重构树:

参考这个讲解。

考虑求 \(Kruskal\) 最小生成树的过程,每次我们枚举一条边然后连接两个点,

我们把这条边变成点,然后边权放到点权上去。我们将连接点所在的子树的根,连到这个点上。

这样有什么性质呢?

  1. 二叉树
  2. 原树与新树两点间路径上边权(点权)的最大值相等
  3. 子节点的边权小于等于父亲节点(大根堆)
  4. 原树中两点之间路径上边权的最大值等于新树上两点的 \(LCA\) 的点权。

这题就是最大生成树,所以是小根堆。

我们主要是可以用第三条性质(其实很好证明,也可以感性理解),\(v\) 所在 \(> p\) 的联通块就是 \(v\) 在重构树上满足点权 \(>p\) 最远的祖先所拥有的子树。

这棵树并不需要显式地建出来,直接隐式地用并查集维护就行了。

这样的话,我们用倍增预处理,并且在合并时每个节点记下子树的 \(min\) 值就行了。

时间复杂度是 \(O(n \log n)\) 的。

代码

其实代码也很好写qwq

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
} void File() {
freopen ("return.in", "r", stdin);
freopen ("return.out", "w", stdout);
} int n, m; const int N = 4e5 + 1e3, M = 8e5 + 1e3; struct Edge {
int u, v, a; inline bool operator < (const Edge &rhs) const {
return a > rhs.a;
} } lt[N]; typedef pair<int, int> PII;
#define fir first
#define sec second
#define mp make_pair namespace Dijkstra { int Head[N], Next[M], to[M], val[M], e = 0; void Init() { Set(Head, 0); e = 0; } inline void add_edge(int u, int v, int w) { to[++ e] = v; Next[e] = Head[u]; val[e] = w; Head[u] = e; } priority_queue<PII, vector<PII>, greater<PII> > P;
bitset<N> vis; int dis[N];
void Run() {
vis.reset();
Set(dis, 0x7f); dis[1] = 0; P.push(mp(0, 1));
while (!P.empty()) {
PII cur = P.top(); register int u = cur.sec; P.pop();
if (vis[u]) continue ; vis[u] = true; for (register int i = Head[u]; i; i = Next[i]) {
register int v = to[i];
if (chkmin(dis[v], dis[u] + val[i]))
P.push(mp(dis[v], v));
}
}
} } namespace Kruskal { int mina[20][N], mind[N], to[20][N], fa[N], Logn, num = 0; void Init(int *bas) {
For (i, 1, n)
fa[i] = i, mind[i] = bas[i]; num = n;
} int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
} inline int Min(int x, int y) {
return x < y ? x : y;
} void Build() { sort(lt + 1, lt + 1 + m); For (i, 1, m) {
int x = lt[i].u, y = lt[i].v, alt = lt[i].a; int rtx = find(x), rty = find(y);
if (rtx == rty) continue ; mind[++ num] = min(mind[rtx], mind[rty]); fa[num] =
to[0][rtx] = fa[rtx] =
to[0][rty] = fa[rty] = num; mina[0][rtx] = mina[0][rty] = alt;
} Logn = ceil(log(num) / log(2));
For (j, 1, Logn) For (i, 1, num) {
to[j][i] = to[j - 1][to[j - 1][i]];
mina[j][i] = Min(mina[j - 1][i], mina[j - 1][to[j - 1][i]]);
} } inline int Query(int pos, int lim) {
Fordown (i, Logn, 0)
if (mina[i][pos] > lim) pos = to[i][pos];
return mind[pos];
} } int q, k, s; int main () {
File(); int cases = read();
while (cases --) { n = read(); m = read(); Dijkstra :: Init(); For (i, 1, m) {
int u = read(), v = read(), l = read(), a = read();
lt[i] = (Edge) {u, v, a};
Dijkstra :: add_edge(u, v, l);
Dijkstra :: add_edge(v, u, l);
}
Dijkstra :: Run(); Kruskal :: Init(Dijkstra :: dis); Kruskal :: Build(); q = read(); k = read(); s = read();
int ans = 0;
while (q --) {
int v = (1ll * read() + k * ans - 1) % n + 1;
int p = (1ll * read() + k * ans) % (s + 1); printf ("%d\n", ans = Kruskal :: Query(v, p));
} } #ifdef zjp_shadow
cerr << (double) clock() / CLOCKS_PER_SEC << endl;
#endif return 0;
}

LOJ #2718. 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增)的更多相关文章

  1. LOJ #2718. 「NOI2018」归程 Dijkstra+可持久化并查集

    把 $Noi2018$ day1t1 想出来还是挺开心的,虽然是一道水题~ 预处理出来 1 号点到其它点的最短路,然后预处理边权从大到小排序后加入前 $i$ 个边的并查集. 这个并查集用可持久化线段树 ...

  2. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  3. 洛谷 4768 LOJ 2718「NOI2018」归程

    [题解] 本题有多种做法,例如可持久化并查集.kruskal重构树等. kruskal重构树的做法是这样的:先把边按照海拔h从大到小的顺序排序,然后跑kruskal建立海拔的最大生成树,顺便建krus ...

  4. [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树)

    [luogu4768] [NOI2018] 归程 (Dijkstra+Kruskal重构树) 题面 题面较长,这里就不贴了 分析 看到不能经过有积水的边,即不能经过边权小于一定值的边,我们想到了kru ...

  5. #2718. 「NOI2018」归程 kruskal重构树

    链接 https://loj.ac/problem/2718 思路 我们希望x所在的连通块尽量的大,而且尽量走高处 离线的话可以询问排序,kruskal过程中更新答案 在线就要用kruskal重构树 ...

  6. Luogu P4768 [NOI2018]归程(Dijkstra+Kruskal重构树)

    P4768 [NOI2018]归程 题面 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 \(n\) 个节点. \(m\) 条边的无向连通图(节点的编 ...

  7. NOI2018d1t1 归程 (dijkstra+kruskal重构树)

    题意:给一张无向联通图,每条边有长度和高度,每次询问在高度大于p的边,从v点能到达的所有点到1号点的最短距离(强制在线) 首先dijkstra求出每个点到1号点的距离 易知:如果我按高度从高到低给边排 ...

  8. P4768 [NOI2018]归程(kruskal 重构树)

    洛谷P4768 [NOI2018]归程 LOJ#2718.「NOI2018」归程 用到 kruskal 重构树,所以先说这是个啥 显然,这和 kruskal 算法有关系 (废话 这个重构树是一个有点权 ...

  9. 「NOI2018」归程

    「NOI2018」归程 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 >\(1\) 个节点. \(m\) 条边的无向连通图(节点的编号从 \( ...

随机推荐

  1. Ionic App之国际化(2) json数组的处理

    在Ionic App值国际化(1)中我们实现了对单个参数的多语言处理,下面开始如何进行数组的处理. 1.在我们的多语言文件中设置要访问的json数组,en.json和zh.json,此处就以en.js ...

  2. Create-React-App 使用记录

    如果要修改 host 和 端口,需要在项目根目录添加 .env 文件,然后再文件中添加 HOST=dev.zhengtongauto.com PORT=3000 如果需要加上反向代理,需要处理接口跨域 ...

  3. Linux ugo 权限

    Linux 系统中文件的 ugo 权限是 Linux 进行权限管理的基本方式.本文将介绍 ugo 权限的基本概念.说明:本文的演示环境为 ubuntu 16.04. 文件的所有者和组 Linux 文件 ...

  4. Oracle日常运维操作总结-数据库的启动和关闭

    下面是工作中对Oracle日常管理操作的一些总结,都是一些基本的oracle操作和SQL语句写法,在此梳理成手册,希望能帮助到初学者(如有梳理不准确之处,希望指出). 一.数据库的启动和关闭 1.1 ...

  5. 北航学堂Android客户端Beta阶段发布说明

    在从学姐那里拿到服务接口的代码最终连通服务器之后,经过我们团队的努力,终于把前后端融合生成了我们目前的版本, 因为我们在Alpha阶段网络连接部分是一直没有搞定的,所以这个版本其实并不算是真正的Bet ...

  6. WEEK 7:团队项目的感想

    经过了几个星期的团队协作,我们的“爬虫”有了很大的完善,我作为团队中的主DEV,在这个过程中一边工作一边阅读,也有了不少的收获. Brooks的<没有银弹>告诉我们,在软件领域,没有什么绝 ...

  7. 《Linux内核设计与实现》 第十八章学习笔记

    调  试 一.准备开始 一个bug 一个藏匿bug的内核版本 相关内核代码的知识和运气 知道这个bug最早出现在哪个内核版本中. 1.想要成功进行调试: 让这些错误重现 抽象出问题 从代码中搜索 二. ...

  8. jisuanqi

    1.jisuanqi 2.https://github.com/12wangmin/text/tree/master 3.计算截图 7+8 清除 4.总结 通过课程设计,主要要达到两个目的,一是检验和 ...

  9. QT QProgressBar QProgressDialog 模态,位置设置,无边框,进度条样式

    一  关于模态设置 QProgressDialog可以设置模态(需要在new的时候传入parent),QProgressBar设置不好: 只有dialog可以设置模态,widget不能设置模态(QPr ...

  10. 小学生四则运算APP核心代码公布

    Mainactivity类: package com.example.XXSCYS; import java.io.ByteArrayOutputStream; import java.io.File ...