首页 > 其他 > 详细

[JZOJ4899] 雪之国度

时间:2019-08-17 00:22:09      阅读:92      评论:0      收藏:0      [点我收藏+]

题目描述

  雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路。雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为Wi。

  如果城市u和v之间有一条道路,那么只要此刻雪之女王的能量不小于|Wu-Wv|,这条道路就是安全的。如果城市u和v之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。

  最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对Q对城市进行调查。对于第j对城市uj和vj,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。

数据范围

  对于 $20\%$ 的数据,$3 \leq N \leq 10$,$3 \leq M \leq 20$,$1 \leq Q \leq 10$

  对于另 $30\%$ 的数据,$W=0$

  对于 $100\%$ 的数据,$3 \leq N \leq 10^5$,$3 \leq M \leq 5 \times 10^5$,$1 \leq Q \leq 10^5$

分析

  这题大意就是问至少经过权值为多少的边,使得两点之间存在两条不经过重复边的路径(边双)

  首先可以建一棵最小生成树,这样每两个点之间有且仅有一条路径,而且权值最大的边一定是在另一条路径(非树边)上

  然后从小到大依次加入非树边,每次加入边一定会形成一个环,由于最小生成树上的边权对答案是无意义的,所以我们可以直接把环上所有没更新过的树边的权值更新为该非树边的权值

  但是如果每次都把环上的所有路径走一遍,就很容易超时,所以需要用并查集维护已经更新过的点,这样就不会对一条边重复访问了

技术分享图片
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100005
#define M 500005
#define T 17

int n, m, q, tot = 1, cnt;
int to[M << 1], nxt[M << 1], head[N];
int fa[N], val[N], used[M], d[N];
int f[N][20], g[N][20];

struct Edge {
    int u, v, w;
} e[M];

bool cmp(Edge a, Edge b) {
    return a.w < b.w;
}

void add(int u, int v) {
    to[++tot] = v; nxt[tot] = head[u]; head[u] = tot;
}

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

void dfs(int x, int ff) {
    d[x] = d[ff] + 1; f[x][0] = ff;
    for (int i = 1; (1 << i) < d[x]; i++)
        f[x][i] = f[f[x][i - 1]][i - 1];
    for (int i = head[x]; i; i = nxt[i])
        if (to[i] != ff) dfs(to[i], x);
}

int lca(int x, int y) {
    int ans = 0;
    if (d[x] < d[y]) swap(x, y);
    for (int i = T; i >= 0; i--)
        if (d[f[x][i]] >= d[y])
            ans = max(ans, g[x][i]), x = f[x][i];
    if (x == y) return ans;
    for (int i = T; i >= 0; i--)
        if (f[x][i] != f[y][i]) {
            ans = max(ans, max(g[x][i], g[y][i]));
            x = f[x][i]; y = f[y][i];
        }
    ans = max(ans, max(g[x][0], g[y][0]));
    return ans;
}

int main() {
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        scanf("%d", val + i);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &e[i].u, &e[i].v);
        e[i].w = abs(val[e[i].u] - val[e[i].v]);
    }
    sort(e + 1, e + m + 1, cmp);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) {
        int x = find(e[i].u), y = find(e[i].v);
        if (x == y) continue;
        fa[x] = y; used[i] = 1;
        add(e[i].u, e[i].v);
        add(e[i].v, e[i].u);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) {
        if (used[i]) continue;
        int x = find(e[i].u), y = find(e[i].v);
        while (x != y) {
            if (d[x] < d[y]) swap(x, y);
            g[x][0] = max(g[x][0], e[i].w);
            fa[x] = f[x][0]; x = find(x);
        }
    }
    for (int i = 1; i <= T; i++)
        for (int j = 1; j <= n; j++)
            g[j][i] = max(g[j][i - 1], g[f[j][i - 1]][i - 1]);
    while (q--) {
        int x, y;
        scanf("%d%d", &x, &y);
        if (find(x) != find(y)) printf("infinitely\n");
        else printf("%d\n", lca(x, y));
    }
    
    return 0;
}
View Code

[JZOJ4899] 雪之国度

原文:https://www.cnblogs.com/Pedesis/p/11366311.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!