题意:
给一个无向图,n个点m条边,每条边都有一个权值,然后q次询问,每次给出一个数值,求用到所有边权不大于这个数值的边的情况下,能够互相到达的点对的个数。
思路:
因为如果点对符合询问中数值较小的,那么肯定要符合数值较大的,所以对所有边按权值小到大排序,所有询问按数值小到大排序。所以根据不断遍历所有小于当前询问的所有边,然后看他们属于几个连通块中,计算点对数其实就是对于一个连通块中取出任意两个点,然后再对所有连通块进行加和。复杂度为O(m+q);
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+5;
struct node
{
int u, v, w;
bool operator<(const node k)const
{
return w < k.w;
}
} e[maxn], p[maxn];
int t, n, m, q;
int f[maxn];
int num[maxn];
ll ans[maxn];
int getF(int x)
{
if(x == f[x]) return x;
int y = x, tmp;
while(f[x] != x) x = f[x];
while(y != f[y])
{
tmp = f[y];
f[y] = x;
y = tmp;
}
return x;
}
int main()
{
for(scanf("%d", &t); t--;)
{
scanf("%d %d %d", &n, &m, &q);
for(int i = 1; i <= n; ++i)
f[i] = i, num[i] = 1;
for(int i = 0; i < m; ++i)
scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
for(int i = 0; i < q; ++i)
scanf("%d", &p[i].w), p[i].u = i;
sort(e, e+m);
sort(p, p+q);
int k = 0; ll sum = 0;
for(int i = 0; i < q; ++i)
{
while(k < m && e[k].w <= p[i].w)
{
int fa = getF(e[k].u), fb = getF(e[k].v);
if(fa != fb)
{
f[fb] = fa;
sum -= num[fa]*(num[fa]-1);
sum -= num[fb]*(num[fb]-1);
num[fa] += num[fb];
sum += num[fa]*(num[fa]-1);
}
++k;
}
ans[p[i].u] = sum;
}
for(int i = 0; i < q; ++i)
printf("%lld\n", ans[i]);
}
return 0;
}
继续加油~