[Noi2018]归程(最短路+Kruskal重构树+倍增)

Solution

如果不强制在线,那么我们可以将边按照海拔高度从大到小排序,询问也按照水位从大到小排序。
这样,没有被淹的边一定是边排序之后的一个前缀,且可通过边的集合随水位的降低而扩大。
预处理出每个点 u 1 的最短路 d i s [ u ] ,在按顺序加边的过程中用并查集维护点之间的连通关系。
这样询问就变成了求在 v 的连通块中求 d i s 的最小值。
如果强制在线,可以考虑可持久化并查集,但这样是 O ( log 2 ) 的,不能通过较大的数据点。
但继续分析可以发现:我们只需要对历史版本进行查询。
首先,对每个点建一棵树,只包含该点。
加边 ( u , v ) 时:
新建节点 w ,用并查集找到 u v 格子所在树的根 R u , R v ,将 w 作为 R u R v 的父亲。定义节点 w 的海拔为边 ( u , v ) 的海拔。
特别地,如果 u v 已经连通,就不要做任何操作。
容易发现, w 的子树内的点就是这次连通之前 w 所代表连通块内的点。
所有边都加入后,建树完成。
我们把这棵树叫做 zzq重构树 Kruskal 重构树。
询问时,如何找到海拔 > p 的边构出的包含 v 的连通块呢?
由 Kruskal 重构树的构建过程可以得出—— v 不断往祖先跳的过程中,海拔高度单调不增。
所以,我们要找的点 w ,就是 v 的祖先中最后一个(深度最浅)的海拔 > p 的点。
特别地,如果 v 的父亲的海拔就 p ,那么 w = v
w 的子树代表了 v 所在的连通块。
记录上子树内 d i s 的最小值就能得出答案。
如果使用倍增找 w ,那么复杂度为线性对数级别。

Code

注意此题卡 SPFA

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define Tree(u) for (int e = adj2[u]; e; e = nxt2[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, M = 8e5 + 15, L = 2e6 + 5,
LogN = 21, INF = 0x3f3f3f3f;
int n, m, ecnt, nxt[M], adj[N], go[M], val[M], que[(M << 2) + M],
len, dis[N], QAQ, ecnt2, nxt2[L], adj2[M], go2[L], sph[M], CNT, fa[M],
F[M][LogN], hei[M];
bool vis[N];
struct pyz {
    int u, dis;
    friend inline bool operator < (pyz a, pyz b) {
        return a.dis > b.dis || (a.dis == b.dis && a.u < b.u);
    }
    friend inline bool operator == (pyz a, pyz b) {
        return a.dis == b.dis && a.u == b.u;
    }
};
priority_queue<pyz> heap, exheap;
struct cyx {
    int u, v, l, a;
} ed[M];
int cx(int x) {
    if (fa[x] != x) fa[x] = cx(fa[x]);
    return fa[x];
}
inline bool comp(const cyx &a, const cyx &b) {
    return a.a > b.a;
}
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; val[ecnt] = w;
}
void add_edge2(int u, int v) {
    nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
void dfs(int u, int fu) {
    int i; F[u][0] = fu;
    For (i, 0, 19) F[u][i + 1] = F[F[u][i]][i];
    Tree(u) dfs(go2[e], u);
}
void orzdalao() {
    while (!heap.empty()) heap.pop();
    while (!exheap.empty()) exheap.pop();
    memset(dis, INF, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    int i; dis[1] = 0;
    heap.push((pyz) {1, 0});
    For (i, 2, n) heap.push((pyz) {i, INF});
    For (i, 1, n) {
        pyz st = heap.top(); vis[st.u] = 1; heap.pop();
        Edge(st.u) if (!vis[v] && dis[st.u] + val[e] < dis[v])
            exheap.push((pyz) {v, dis[v]}),
            dis[v] = dis[st.u] + val[e],
            heap.push((pyz) {v, dis[v]});
        while (!heap.empty() && !exheap.empty()
            && heap.top() == exheap.top())
                heap.pop(), exheap.pop();
    }
}
void work() {
    ecnt = ecnt2 = 0; memset(adj, 0, sizeof(adj));
    memset(adj2, 0, sizeof(adj2));
    int i, u, v, l, a, lst = 0; CNT = n = read(); m = read();
    For (i, 1, m) u = read(), v = read(), l = read(), a = read(),
        add_edge(u, v, l), ed[i] = (cyx) {u, v, l, a};
    sort(ed + 1, ed + m + 1, comp); orzdalao();
    For (i, 1, n) sph[fa[i] = i] = dis[i], hei[i] = INF;
    For (i, 1, m) {
        int frx = cx(ed[i].u), fry = cx(ed[i].v);
        if (frx == fry) continue;
        fa[++CNT] = CNT;
        add_edge2(CNT, frx); add_edge2(CNT, fry);
        hei[CNT] = ed[i].a;
        sph[CNT] = min(sph[frx], sph[fry]);
        fa[frx] = fa[fry] = CNT;
    }
    dfs(CNT, 0);
    int p, q, K, S; q = read(); K = read(); S = read();
    while (q--) {
        v = read(); p = read();
        v = (1ll * K * lst + n + v - 1) % n + 1;
        p = (1ll * K * lst + p) % (S + 1);
        Rof (i, 20, 0) {
            if (!F[v][i]) continue;
            if (hei[F[v][i]] > p) v = F[v][i];
        }
        printf("%d\n", lst = sph[v]);
    }
}
int main() {
    //freopen("return.in", "r", stdin);
    //freopen("return.out", "w", stdout);
    int T = read(); while (T--) work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81169155