zoukankan      html  css  js  c++  java
  • 【NOI 2018】归程(Kruskal重构树)

    题面在这里就不放了。

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

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

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

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

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

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

    先给出一个经典的模型:

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

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

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

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

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

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

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

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

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

      1 #include <cstdio>
      2 #include <queue>
      3 #include <cstring>
      4 #include <algorithm>
      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<std::pair<int, int> > 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
    ", mdi[x]);
    113             lans = mdi[x];
    114         }
    115     }
    116     
    117     return 0;
    118 }
    View Code

    $igodot$技巧&套路:

    • kruskal重构树的构建,最小生成树上最值问题的再探。
  • 相关阅读:
    JS Leetcode 530. 二叉搜索树的最小绝对差 题解分析,再次了解中序遍历
    JS Leetcode 179. 最大数 题解分析,sort a-b与b-a的区别,sort排序原理解析
    Echarts 数据可视化 (二)
    Echarts 数据可视化 (一)
    Less 入门
    【Mybatis-Plus框架学习】专栏总集篇
    【Mybatis-Plus框架学习】雪花算法 剖析
    【多文件自平衡云传输】使用展示 —— 文件传输系统
    详解 资源发现技术 的基本实现
    【多文件自平衡云传输】专栏总集篇
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9333015.html
Copyright © 2011-2022 走看看