题意:给你n个点,m条带权边,现在q次询问,每次询问给你一个val,让你求有多少对点能相连且路径上最大权不能
超过val。(ab和ba算两对)(n<=2e4, m<=1e5, q<=5e3)
思路:把每条边按权值从小到大排序,按每次询问val值从小到大排序,枚举询问时,不断加上<=val的边。用并查集
求得每次加一条边后新产生的点对。用一个数组num[i],来记录i这个联通块的点的数量。每次两个联通块相连,新增
加的点对数为num[a]*num[b]*2.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int pre[maxn], n, m, q;
ll cnt, num[maxn], ans[maxn];
struct node1
{
int u, v, w;
bool operator < (const node1 &a) const
{
return w < a.w;
}
}edge[maxn];
struct node2
{
int d, id;
bool operator < (const node2 &a) const
{
return d < a.d;
}
}op[maxn];
int Find(int x)
{
int r = x;
while(pre[r] != r) r = pre[r];
int i = x, j;
while(i != r)
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}
void join(int x, int y)
{
int a = Find(x);
int b = Find(y);
if(a != b)
{
pre[b] = a;
cnt += num[b]*num[a]*2;
num[a] += num[b];
}
}
int main(void)
{
int t;
cin >> t;
while(t--)
{
cnt = 0;
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i++)
pre[i] = i, num[i] = 1;
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
for(int i = 1; i <= q; i++)
scanf("%d", &op[i].d), op[i].id = i;
sort(edge+1, edge+1+m);
sort(op+1, op+1+q);
for(int i = 1, j = 1; i <= q; i++)
{
while(edge[j].w <= op[i].d)
join(edge[j].u, edge[j].v), j++;
ans[op[i].id] = cnt;
}
for(int i = 1; i <= q; i++)
printf("%lld\n", ans[i]);
}
return 0;
}
n
≤
20000
,
m
≤
100000
,
q
≤
5000