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

1011

被折叠的 条评论
为什么被折叠?



