离线+带权并查集 hdoj3938 Portal

题目传送门: http://acm.hdu.edu.cn/showproblem.php?pid=3938

题目的题意有些不清楚,看了discuss才明白。定义图上一条路径的cost为路径中所有边权中最大的那个。对于给定的图,回答当最大的路径长度为l的时候,最多可以有多少顶点对是可及的。

按照Kruskal的思路,按边权从小到大连接顶点,每次相连,如果减少一个连通分量,那么可及顶点对就会增大,增大的个数就是原来两个连通分量顶点个数的乘积,不断累加就可以得到对于不同的l,一共有多少顶点对。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int M = 5e4 + 5;
const int N = 1e4 + 5;
int father[N], sum[N], ans[N];
int n, m, t;
struct edge {
    int u, v, w;
} e[M];
struct query {
    int id, l;
} q[N];
bool cmp(const edge &e1, const edge &e2)
{
    return e1.w < e2.w;
}
bool cmp1(const query &q1, const query &q2)
{
    return q1.l < q2.l;
}
int find(int x)
{
    while (x != father[x]) {
        father[x] = father[father[x]];
        x = father[x];
    }
    return x;
}
int merge(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y) {
        father[y] = x;
        sum[x] += sum[y];
        return (sum[x] - sum[y]) * sum[y];
    } else return 0;
}
void init(void)
{
    for (int i = 1; i <= n; i ++) {
        sum[i] = 1;
        father[i] = i;
    }
}
int main()
{
    while (~scanf("%d%d%d", &n, &m, &t)) {
        init();
        for (int i = 1; i <= m; i ++) {
            scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
        }
        sort(e + 1, e + 1 + m, cmp);
        for (int i = 1; i <= t; i ++) {
            scanf("%d",&q[i].l);
            q[i].id = i;
        }
        sort(q + 1, q + 1 + t, cmp1);
        int cnt = 0, j = 1;
        for (int i = 1; i <= t; i ++) {
            while (j <= m && e[j].w <= q[i].l) {
                cnt += merge(e[j].u, e[j].v);
                ++ j;
            }
            ans[q[i].id] = cnt;
        }
        for (int i = 1; i <= t; i ++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值