Kruskal重构树及 NOI2018D1T1 归程

Kruskal重构树,就是在运行Kruskal算法的时候,对于每次找到的一条边{x, y, w},我们新建一个节点z,权值为边权w,连接fa[x], z和fa[y], z,然后用并查集的方式把x和y各自所在的集合都合并到z里,下一次的fa[x]和fa[y]就是z了。

inline void kruskal()
{
    for (int i = 1; i < n << 1; i++) fa[i] = i;
    int cnt = n;
    sort(e + 1, e + m + 1);
    for (int i = 1; i <= m; i++) {
        if (cnt == 2 * n - 1) break;
        int x = e[i].x, y = e[i].y, u = find(x), v = find(y);
        if (u != v) {
            fa[u] = fa[v] = ++cnt; //新节点以及维护集合关系
            val[cnt] = e[i].z; //新节点的权值为原来的边权
            add_edge(u, cnt);
            add_edge(cnt, u);
            add_edge(v, cnt);
            add_edge(cnt, v); //连边
        }
    }
}

叶子节点就对应着原图的点,非叶子节点就对应着原图的边。
这样做的话,有一些很好的性质:
1、二叉树,不过没啥用。
2、任何一个节点到跟的路径所经过的点的权值是单调的
3、原图中任意两点之间使得最大值最小的路径的那个最大值,对应的就是重构树上的lca(x, y)的权值,这一点和性质2是一致的。

利用上述性质,我们来做今年的签到题。
原题
首先预处理出所有点到1的最短路dis,然后按海拔从大到小的顺序把每条边加入Kruskal重构树中,那么,对于一次询问的高度h,我们就用倍增找到最靠上的一个点使这个点的权值还大于h,这个点的子树内的所有点就都是x能到的点了,树形dp预处理出每个节点子树内最小的dis就好了。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>

const int maxn = 2e5 + 7;
const int maxm = 4e5 + 7;

using namespace std;

typedef pair <int, int> pii;

int n, m;
int dis[maxn << 1];
int vis[maxn];
int to1[maxm << 1];
int val1[maxm << 1];
int nex1[maxm << 1];
int last1[maxn], k1;
struct edge {
    int x, y, z;
    inline int operator < (const edge a) const {
        return z > a.z;
    }
} e[maxm];
int fa[maxn << 1];
int val[maxn << 1];
int to[maxn << 2];
int nex[maxn << 2];
int last[maxn << 1], k;
int dep[maxn << 1];
int rmq[maxn << 1][21];

inline int read()
{
    int X = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
    return X;
}

inline void add_edge(int x, int y, int z)
{
    to1[++k1] = y; val1[k1] = z; nex1[k1] = last1[x]; last1[x] = k1;
}

inline void dijkstra()
{
    memset(dis, 0x3f, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[1] = 0;
    priority_queue <pii, vector <pii>, greater <pii> > q;
    q.push(make_pair(dis[1], 1));
    while (!q.empty()) {
        pii tmp = q.top();
        q.pop();
        int x = tmp.second;
        if (!vis[x]) {
            vis[x] = 1;
            for (int i = last1[x]; i; i = nex1[i]) {
                int y = to1[i];
                if (dis[x] + val1[i] < dis[y]) {
                    dis[y] = dis[x] + val1[i];
                    q.push(make_pair(dis[y], y));
                }
            }
        }
    }
}

int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

inline void add_edge(int x, int y)
{
    to[++k] = y; nex[k] = last[x]; last[x] = k;
}

inline void kruskal()
{
    for (int i = 1; i < n << 1; i++) fa[i] = i;
    int cnt = n;
    sort(e + 1, e + m + 1);
    for (int i = 1; i <= m; i++) {
        if (cnt == 2 * n - 1) break;
        int x = e[i].x, y = e[i].y, u = find(x), v = find(y);
        if (u != v) {
            fa[u] = fa[v] = ++cnt;
            val[cnt] = e[i].z;
            dis[cnt] = min(dis[u], dis[v]);
            add_edge(u, cnt);
            add_edge(cnt, u);
            add_edge(v, cnt);
            add_edge(cnt, v);
        }
    }
}

void dfs(int x, int f)
{
    rmq[x][0] = f;
    dep[x] = dep[f] + 1;
    for (int i = 1; 1 << i <= dep[x]; i++)
        rmq[x][i] = rmq[rmq[x][i - 1]][i - 1];
    for (int i = last[x]; i; i = nex[i])
        if (to[i] != f) dfs(to[i], x);
}

inline int jump(int x, int h)
{
    for (int i = 20; ~i; i--)
        if (rmq[x][i] && val[rmq[x][i]] > h) x = rmq[x][i];
    return x;
}

int main(void)
{
    int t = read();
    while (t--) {
        cin >> n >> m;
        k = k1 = 0;
        memset(last1, 0, sizeof last1);
        memset(last, 0, sizeof last);
        memset(rmq, 0, sizeof rmq);
        for (int i = 1; i <= m; i++) {
            int x = read(), y = read(), z = read(), h = read();
            add_edge(x, y, z);
            add_edge(y, x, z);
            e[i] = {x, y, h};
        }
        dijkstra();
        kruskal();
        dfs(2 * n - 1, 0);
        int q = read(), K = read(), S = read(), ans = 0;
        while (q--) {
            int x = (read() + K * ans - 1) % n + 1;
            int h = (read() + K * ans) % (S + 1);
            printf("%d\n", ans = dis[jump(x, h)]);
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/star_city_7/article/details/81310205