题面在这里就不放了。

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

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

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

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

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

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

先给出一个经典的模型:

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

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

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

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

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

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

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

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

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

 #include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm> typedef long long LL;
const int N = , INF = 2e9 + , LOG = ; int tc, n, m, Qi, k, s;
int dis[N], flg[N], val[N], mdi[N], gr[LOG][N];
std::priority_queue<std::pair<int, int> > Q; inline void Read(int &x) {
x = ; static char c;
for (c = getchar(); c < '' || c > ''; c = getchar());
for (; c >= '' && c <= ''; x = (x << ) + (x << ) + c - '', c = getchar());
} struct Edge {
int u, v, a;
inline friend bool operator < (Edge a, Edge b) {
return a.a > b.a;
}
} e[N]; int yun, las[N], to[N << ], pre[N << ], wi[N << ];
inline void Add(int a, int b, int c = ) {
to[++yun] = b; wi[yun] = c; pre[yun] = las[a]; las[a] = yun;
}
void Gragh_clear() {
memset(gr, , sizeof gr);
memset(las, , sizeof las);
yun = ;
} namespace DSU {
int fa[N];
void Init() {
for (int i = ; i <= n + m; ++i) {
fa[i] = i;
if (i <= n) mdi[i] = dis[i], val[i] = -;
}
}
int Seek(int x) {
return (x == fa[x])? (x) : (fa[x] = Seek(fa[x]));
}
void Merge(int x, int y) {
fa[Seek(y)] = x;
}
} void Dij() {
for (int i = ; i <= n; ++i) {
dis[i] = INF; flg[i] = ;
}
dis[] = ;
Q.push(std::make_pair(, ));
for (; !Q.empty(); ) {
int x = Q.top().second; Q.pop();
if (flg[x]) continue;
flg[x] = ;
for (int i = las[x]; i; i = pre[i]) {
if (dis[to[i]] > dis[x] + wi[i]) {
dis[to[i]] = dis[x] + wi[i];
Q.push(std::make_pair(-dis[to[i]], to[i]));
}
}
}
} int main() {
freopen("return.in", "r", stdin);
freopen("return.out", "w", stdout); scanf("%d", &tc);
for (; tc; --tc) {
scanf("%d%d", &n, &m);
Gragh_clear();
for (int i = , x, y, a, l; i <= m; ++i) {
//scanf("%d%d%d%d", &x, &y, &l, &a);
Read(x); Read(y); Read(l); Read(a);
Add(x, y, l); Add(y, x, l);
e[i] = (Edge) { x, y, a };
}
Dij(); DSU::Init(); std::sort(e + , e + + m);
for (int i = ; i <= m; ++i) {
int x = DSU::Seek(e[i].u), y = DSU::Seek(e[i].v);
if (x == y) continue;
val[i + n] = e[i].a;
mdi[i + n] = std::min(mdi[x], mdi[y]);
DSU::Merge(i + n, x); DSU::Merge(i + n, y);
gr[][x] = gr[][y] = i + n;
} for (int i = ; i < LOG; ++i) {
for (int j = ; j <= n + m; ++j) {
if (gr[i - ][j]) gr[i][j] = gr[i - ][gr[i - ][j]];
}
} scanf("%d%d%d", &Qi, &k, &s);
for (int x, a, lans = ; Qi; --Qi) {
//scanf("%d%d", &x, &a);
Read(x); Read(a);
x = (x + (LL) k * lans - ) % n + ;
a = (a + (LL) k * lans) % (s + );
for (int i = LOG - ; ~i; --i) {
if (gr[i][x] && val[gr[i][x]] > a) x = gr[i][x];
}
printf("%d\n", mdi[x]);
lans = mdi[x];
}
} return ;
}

$\bigodot$技巧&套路:

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

【NOI 2018】归程(Kruskal重构树)的更多相关文章

  1. NOI 2018 归程 (Kruskal重构树)

    题目大意:太长了,略 Kruskal重构树,很神奇的一个算法吧 如果两个并查集被某种条件合并,那么这个条件作为一个新的节点连接两个并查集 那么在接下来的提问中,如果某个点合法,它的所有子节点也都合法, ...

  2. NOI Day1T1归程(Kruskal重构树+Dijkstra)

    NOI Day1T1归程(Kruskal重构树+Dijkstra) 题目 洛谷题目传送门 题解 其实我不想写......,所以...... 挖个坑......我以后一定会补的 luogu的题解讲的还是 ...

  3. BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...

  4. [NOI2018]归程 kruskal重构树

    [NOI2018]归程 LG传送门 kruskal重构树模板题. 另一篇文章里有关于kruskal重构树更详细的介绍和更板子的题目. 题意懒得说了,这题的关键在于快速找出从查询的点出发能到达的点(即经 ...

  5. [洛谷P4768] [NOI2018]归程 (kruskal重构树模板讲解)

    洛谷题目链接:[NOI2018]归程 因为题面复制过来有点炸格式,所以要看题目就点一下链接吧\(qwq\) 题意: 在一张无向图上,每一条边都有一个长度和海拔高度,小\(Y\)的家在\(1\)节点,并 ...

  6. 洛谷P4768 [NOI2018]归程(Kruskal重构树)

    题意 直接看题目吧,不好描述 Sol 考虑暴力做法 首先预处理出从$1$到每个节点的最短路, 对于每次询问,暴力的从这个点BFS,从能走到的点里面取$min$ 考虑如何优化,这里要用到Kruskal重 ...

  7. LOJ.2718.[NOI2018]归程(Kruskal重构树 倍增)

    LOJ2718 BZOJ5415 洛谷P4768 Rank3+Rank1无压力 BZOJ最初还不是一道权限题... Update 2019.1.5 UOJ上被hack了....好像是纯一条链的数据过不 ...

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

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

  9. loj2718 「NOI2018」归程[Kruskal重构树+最短路]

    关于Kruskal重构树可以翻阅本人的最小生成树笔记. 这题明显裸的Kruskal重构树. 然后这题限制$\le p$的边不能走,实际上就是要满足走最小边权最大的瓶颈路,于是跑最大生成树,构建Krus ...

  10. BZOJ 5415: [Noi2018]归程(kruskal重构树)

    解题思路 \(NOI2018\)的\(Day1\) \(T1\),当时打网络赛的时候不会做.学了一下\(kruskal\)重构树后发现问题迎刃而解了.根据\(kruskal\)的性质,如果要找从\(u ...

随机推荐

  1. Python输出格式全总结

    输入输出 有几种方法可以显示程序的输出:数据可以以人类可读的形式打印出来,或者写入文件以供将来使用.本章将讨论一些可能性. 更漂亮的输出格式 到目前为止,我们遇到了两种写入值的方法:表达式语句 和 p ...

  2. [转载]MySQL面试题

    1.MySQL的复制原理以及流程基本原理流程,3个线程以及之间的关联:(1)主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中:(2)从:io线程——在使用s ...

  3. 天马行空云计算(二)-Hardware&Hypervisor介绍

    天马行空云计算系列一介绍了总体抽象视图,本篇展开Hardware&Hypervisor 介绍.如下是介绍大纲: 本篇将基于上述架构从如下方面介绍说明 Linux设备驱动 因为上述提到的一些硬件 ...

  4. spring-boot rabbitMq 完整项目搭建,包括创建、发送、监听

    写在开始 rabbitMq 代码按照三部分介绍 第一部分 交换机和队列的创建 第二部分 消息发送 第三部分 消息监听 第一部分 1 建立queue 2 建立exchange 3 exchange绑定q ...

  5. 用python实现数字图片识别神经网络--启动网络的自我训练流程,展示网络数字图片识别效果

    上一节,我们完成了网络训练代码的实现,还有一些问题需要做进一步的确认.网络的最终目标是,输入一张手写数字图片后,网络输出该图片对应的数字.由于网络需要从0到9一共十个数字中挑选出一个,于是我们的网络最 ...

  6. python基础_字符编码

    字符编码的历史 阶段一:现代计算机起源于美国,最早诞生也是基于英文考虑的ASCII 阶段二:为了满足中文,中国人定制了GBK 阶段三:各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的 ...

  7. Java中的常见异常

    非检查异常:Error 和 RuntimeException 以及他们的子类.0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界Ar ...

  8. Spring下使用开发webservice

    依赖包 <!-- CXF Dependencies --> <dependency> <groupId>org.apache.cxf</groupId> ...

  9. C语言中的strstr函数

    转自:http://www.cnblogs.com/xy-kidult/archive/2012/12/25/2832460.html 早上翻<C和指针>,碰见一个子串查找问题,这个问题在 ...

  10. Beta阶段团队项目开发篇章3

    例会时间 2016.12.6晚 例会照片 个人工作 上阶段任务验收 中英文切换功能已经实现,调查结果分析已经完成,博客基本撰写完成,在征求其他组员意见后发布.任务基本完成. 任务分配 组员 任务内容 ...