BZOJ 3732: Network

2 篇文章 0 订阅
1 篇文章 0 订阅

Description

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).

现在有 K个询问 (1 < = K < = 20,000)。
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Input

第一行: N, M, K。
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。
第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Output

对每个询问,输出最长的边最小值是多少。

Sample Input

6 6 8

1 2 5

2 3 4

3 4 3

1 4 8

2 5 7

4 6 2

1 2

1 3

1 4

2 3

2 4

5 1

6 2

6 1

Sample Output

5

5

5

4

4

7

4

5

HINT

1 <= N <= 15,000

1 <= M <= 30,000

1 <= d_j <= 1,000,000,000

1 <= K <= 15,000

分析

在一个图中任意两点都有很多条路,我们要找出其中一条路,使这条路上的最大的权值是其他路上最大权值中最小的。而我们要保证权值最小,但是还是当前路上最大。由于这里没有限制边数,我们会想到。树上的一个有趣的模型——最小生成树。这棵树上每条路都是整个图中较小的边,而我们的问题就转化成了在最小生成树上找最大边。这个时候就可以用倍增的思想找LCA。在一棵树上两点之间最短距离一定经过它们的LCA。所以我们找出2点的LCA而且维护这条最短路径上的最大边即可。

代码

#include <bits/stdc++.h>

#define N 100010

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

int n,m,q;

struct NODE
{
    int x,y,val;
}t[N];

bool cmp(NODE a,NODE b)
{
    return a.val < b.val;
}

struct EDGE
{
    int to,next,val;
}e[N * 10];

int cnt;
int next[N * 10];

struct NOTE
{
    int val;
    int num;
}lca[21][N];

int f[N];

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

int vis[N],dep[N * 10];

void add(int x,int y,int z)
{
    e[++cnt] = (EDGE){y,next[x],z}; next[x] = cnt;
    e[++cnt] = (EDGE){x,next[y],z}; next[y] = cnt;
}

void dfs(int x,int d)
{
    vis[x] = 1;
    dep[x] = d;
    for (int i = next[x]; i; i = e[i].next)
    {
        if (vis[e[i].to])
            continue;
        lca[0][e[i].to].num = x;
        lca[0][e[i].to].val = e[i].val;
        dfs(e[i].to, d + 1);
    }
}

int LCA(int x,int y)
{
    int minVal = -1;
    if (dep[x] > dep[y])
        std::swap(x,y);
    for (int i = 20; ~i; i--)
    {
        if (dep[lca[i][y].num] >= dep[x])
        {
            minVal = std::max(minVal, lca[i][y].val);
            y = lca[i][y].num;
        }
    }
    if (y == x)
        return minVal;
    for (int i = 20; ~i; i--)
    {
        if (lca[i][y].num != lca[i][x].num)
        {
            minVal = std::max(minVal, std::max(lca[i][y].val, lca[i][x].val));
            y = lca[i][y].num;
            x = lca[i][x].num;
        }
    }
    minVal = std::max(minVal, std::max(lca[0][x].val, lca[0][y].val));
    return minVal;
}

int getTree()
{
    std::sort(t + 1, t + m + 1, cmp);
    for (int i = 1; i <= n; i++)
        f[i] = i;
    int tot = 0;
    for (int i = 1; i <= m; i++)
    {
        int x = find(t[i].x), y = find(t[i].y);
        if (x == y)
            continue;
        f[y] = x;
        add(t[i].x, t[i].y, t[i].val);
        ++tot;
        if (tot == n - 1)
            break;
    }
    lca[0][tot / 2].num = tot / 2;
    lca[0][tot / 2].val = 0;
    dfs(tot / 2, 1);
}

int main()
{
    n = read(), m = read(), q = read();
    for (int i = 1; i <= m; i++)
        t[i].x = read(), t[i].y = read(), t[i].val = read();
    getTree();
    for (int i = 1; i <= 20; i++)
        for (int j = 1; j <= n; j++)
        {
            lca[i][j].val = std::max(lca[i - 1][j].val, lca[i - 1][lca[i - 1][j].num].val);
            lca[i][j].num = lca[i - 1][lca[i - 1][j].num].num;
        }
    for (int i = 1; i <= q; i++)
    {
        int x = read(), y = read();
        printf("%d\n",LCA(x,y));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值