旅游
程序员文章站
2022-03-15 22:34:18
...
题目链接:
题目
输入
输出
样例输入
1
5 5 3
2 3 6334
1 5 15724
3 5 5705
4 3 12382
1 3 21726
6000
10000
13000
样例输出
2
6
12
数据范围
思路
这道题其实是一道并查集。
我们就把所有的边按边权从小到大排序,然后一条一条看,如果两边不连通,就相连,一边到另外一边的最大人气就是这条边的人气。
(挺像最小生成树的,我觉得)
那怎么看答案呢?
设 为最大容忍限度为 的时候答案是多少,再设 为 所在的集合里面有多少个点。
那我们连边的时候, 就是两个原来的 相加,而 。
( 就是这条边的人气值,乘 因为 和 算两个)
(就是左边任意的点走到右边任意的点加上右边任意的点走到左边任意的点)
然后我们把 从前缀和一下,就可以直接读入 然后输出 了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int T;
struct node {
int x, y, cost;
}a[200001];
int n, m, q, fa[200001], maxn, ans[200001], num[200001], x;
int read() {//快读
int an = 0, * = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') * = -*;
c = getchar();
}
while (c >= '0' && c <= '9') {
an = an * 10 + c - '0';
c = getchar();
}
return an * *;
}
bool cmp(node x, node y) {//按边权排序
return x.cost < y.cost;
}
int find(int now) {//并查集
if (fa[now] == now) return now;
return fa[now] = find(fa[now]);
}
void work() {
int X, Y;
for (int i = 1; i <= m; i++) {//枚举边
X = find(a[i].x);
Y = find(a[i].y);
if (X == Y) continue;//看是否连通
fa[X] = Y;
ans[a[i].cost] += num[X] * num[Y] * 2;//求出新答案
num[Y] += num[X];//求出新长度
}
}
int main() {
T = read();//读入
for (int times = 1; times <= T; times++) {
memset(ans, 0, sizeof(ans));//初始化
n = read();//读入
m = read();
q = read();
for (int i = 1; i <= m; i++) {
a[i].x = read();//读入
a[i].y = read();
a[i].cost = read();
maxn = max(maxn, a[i].cost);//记录
}
sort(a + 1, a + m + 1, cmp);//按边权排序
for (int i = 1; i <= n; i++) {//初始化
fa[i] = i;
num[i] = 1;
}
work();
for (int i = 2; i <= maxn; i++)//前缀和
ans[i] += ans[i - 1];
for (int i = 1; i <= q; i++) {
x = read();//读入
printf("%d\n", ans[x]);//输出
}
}
return 0;
}