The 15th Chinese Northeast Collegiate Programming Contest -- K.City 题解

文章描述了解决一个图论问题的方法,通过并查集数据结构维护节点连接情况,根据边权值降序处理查询,计算在满足条件的边连接后,不同集合间点对的数量。

题目大意:

n 个城市由 m 条无向边连接,每条边的边权为 k,现在有 q 次询问,每次询问给一个整数 p,只有边权 >= p 的边可以留下来,问留下的边构成的图里有多少对 两两相连的点。

解题思路:

运用并查集可以维护连接起的点的集合,顺便还可以记录每个集合的点数,方便计算答案。

存边:运用pair<int, pair<int, int>> 存图,键存边权,值存端点

存询问:运用pair<int, int> 键存p,值存编号

将其存好后都降序排列(能做到先连接边权大的边,再接小边 -- 简化计算过程),根据p,连接保留下来的边,对于不在同一集合的点先更新答案 -- 答案加上两集合点数乘积,再将其放到同一集合中,并更新其内部点的数量。

ac代码:

// #include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <bitset>
#include <vector>
#include <cstring>
#include <cmath>
#include <set>
using namespace std;
typedef long long LL;

const int N = 2e5 + 5;
int n, m, q;
pair<int, pair<int, int>> g[N]; // 存图,关键字存边权
pair<int, int> que[N]; // 存询问, 关键词存询问编号
int p[N];// 存祖宗结点
LL s[N], ans[N];
// s-存当前集合中点的数量  ans-存询问的答案

void init()
{
    for (int i = 1; i <= n; i++)
    {
        p[i] = i;
        s[i] = 1;
    }
    memset(ans, 0, sizeof ans);
}

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

void solve()
{
    cin >> n >> m >> q;
    init();
    for (int i = 0; i < m; i++)
    {
        int a, b, k;
        cin >> a >> b >> k;
        g[i] = {k, {a, b}};
    }
    sort(g, g + m);
    reverse(g, g + m);

    for (int i = 0; i < q; i++)
    {
        int p;
        cin >> p;
        que[i] = {p, i};
    }
    sort(que, que + q);
    reverse(que, que + q);

    LL res = 0;
    int l = 0;
    for (int i = 0; i < q; i++)
    {
        int di = que[i].first;
        while (g[l].first >= di && l < m)
        {
            int a = g[l].second.first, b = g[l].second.second;
            if (find(a) != find(b))
            {
                res += (LL)s[find(a)] * s[find(b)];
                s[find(b)] += s[find(a)];
                p[find(a)] = find(b);
            }
            l++;
        }
        ans[que[i].second] = res;
    }

    for (int i = 0; i < q; i++)
        cout << ans[i] << "\n";
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }

    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值