「Kruskal 重构树」「NOI2018」归程

知识点: Kruskal 重构树,最短路

原题面 Loj Luogu

简述

简不动。

分析

只能开车经过海拔 \(\ge\) 水位的边,考虑 Kruscal 重构树。
边权 \(\rightarrow\) 点权,重构树上的点权值 自根向叶子递减。

对于从 \(v\) 出发,水位为 \(p\) 的询问,考虑从起点倍增到点权值 \(\le p\) 的,点权最大的祖先 \(u\)
显然,\(u\) 的子树中所有节点的点权 \(\le p\)
开车从 \(v\) 出发,可到达 \(u\) 子树中所有节点。
若 终点 \(1\)\(u\) 子树中,显然步行经过的长度为 \(0\)
否则,应该从能够开车到达的节点中,选择距离 \(1\) 最近的节点,步行过去。

预处理每个节点与 \(1\) 的最短路。
维护 Kruscal 重构树每棵子树中,最短的最短路。
查询时输出祖先 \(u\) 的维护信息即可。

总复杂度 \(O(m\log m + Q\log n)\)

关于 spfa,他____了。

代码

//知识点:Kruskal 重构树,最短路
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
const int kMaxn = 5e5 + 10;
//=============================================================
struct Edge {
  int u, v, w, ne;
} e[kMaxn << 1];
struct KruscalEdge {
  int u, v;
  ll lim;
} e1[kMaxn];
int n, m, q, edge_num, root, head[kMaxn];
int bel[kMaxn], dep[kMaxn], fa[kMaxn][30];
ll val[kMaxn], ans[kMaxn];
bool vis[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(ll &fir, int sec) {
  if (sec < fir) fir = sec;
}
bool Compare(KruscalEdge fir, KruscalEdge sec) {
  return fir.lim > sec.lim;  //注意排序对象
}
void AddEdge(int u, int v, ll w) {
  e[++edge_num] = (Edge){u, v, w, head[u]};
  head[u] = edge_num;
}
int Find(int x) { return bel[x] == x ? x : bel[x] = Find(bel[x]); }
void Union(int x, int y, ll lim) {
  int r1 = Find(x), r2 = Find(y);
  ++root;
  bel[r1] = bel[r2] = bel[root] = root, val[root] = lim;
  AddEdge(root, r1, 0), AddEdge(root, r2, 0);
}
void Clear() {
  edge_num = root = 0;
  memset(head, 0, sizeof(head));
  memset(fa, 0, sizeof(fa));
}
void Prepare() {
  Clear();
  root = n = read(), m = read();
  for (int i = 1; i <= n; ++i) bel[i] = i;
  for (int i = 1; i <= m; ++i) {
    int u = read(), v = read();
    ll w = read(), lim = read();
    e1[i] = (KruscalEdge){u, v, lim};
    AddEdge(u, v, w), AddEdge(v, u, w);
  }
}
void Dijkstra() {
  std ::priority_queue<std ::pair<ll, int> > q;
  memset(ans, 127, sizeof(ans));
  memset(vis, 0, sizeof(vis));
  ans[1] = 0;
  q.push(std ::make_pair(0, 1));

  while (!q.empty()) {
    int u = q.top().second;
    q.pop();
    if (vis[u]) continue;
    vis[u] = 1;

    for (int i = head[u]; i; i = e[i].ne) {
      int v = e[i].v;
      ll w = e[i].w;
      if (ans[v] > ans[u] + w) {
        ans[v] = ans[u] + w;
        q.push(std ::make_pair(-ans[v], v));
      }
    }
  }
}
void Kruscal() {
  memset(head, 0, sizeof(head));
  edge_num = 0;
  std ::sort(e1 + 1, e1 + m + 1, Compare);
  for (int i = 1; i <= m; ++i) {
    int u = e1[i].u, v = e1[i].v;
    ll lim = e1[i].lim;
    if (Find(u) == Find(v)) continue;
    Union(u, v, lim);
  }
}
void Dfs(int u) {
  for (int i = 1; (1 << i) <= dep[u]; ++i) {
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
  }
  for (int i = head[u]; i; i = e[i].ne) {
    dep[e[i].v] = dep[u] + 1, fa[e[i].v][0] = u;
    Dfs(e[i].v);
    GetMin(ans[u], ans[e[i].v]);
  }
}
void Query() {
  int Q = read(), K = read(), S = read();
  ll lastans = 0;
  for (int i = 1; i <= Q; ++i) {
    int v = (read() + K * lastans - 1) % n + 1,
        p = (read() + K * lastans) % (S + 1);
    for (int j = 20; j >= 0; j--) {
      if (fa[v][j] && val[fa[v][j]] > p) {
        v = fa[v][j];
      }
    }
    printf("%lld\n", lastans = ans[v]);
  }
}
//=============================================================
int main() {
  freopen("return.in", "r", stdin);
  freopen("return.out", "w", stdout);
  int T = read();
  while (T--) {
    Prepare();
    Dijkstra();
    Kruscal();
    Dfs(root);
    Query();
  }
  return 0;
}
posted @ 2020-08-31 11:42  Luckyblock  阅读(99)  评论(1编辑  收藏  举报