题意:
给定N个顶点,M条边的一个无向图,Q个询问。
对于每个询问x,从a,b的路径上各边的最大权值小于x,可以记为有序对<a,b>, 求这个图里面有多少个这样的有序对。
输入:
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
分析:
在N和M都比较大情况下,肯定遍历是不行的。把图画出来,我们把权值小于x的边的两端视为连通,那么对于每个连通的部分,有序对的个数就是连通部分里面顶点个数n*(n-1),对于整个图来说,我们只需要把各个连通部分的有序对个数相加就行了。
这样思路就出来了,首先边排序,然后并查集处理各顶点之间的连通关系,离线处理各个询问就行了。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
const int maxn=20000+5;
const int maxm=100000+5;
const int maxq=5005;
struct edge //将边进行排序
{
int from,to,cost;
bool operator < (const edge &t) const
{
return cost<t.cost;
}
} edges[maxm];
int pa[maxn],num[maxn],Query[maxq],qq[maxq];
map<int,ll> Ans;
int Find(int x) //查找--路径压缩
{
return pa[x]==x?x:(pa[x]=Find(pa[x]));
}
int main()
{
int T,N,M,Q;
scanf("%d",&T);
while(T--)
{
scanf("%d %d %d", &N, &M, &Q);
int u, v, c;
for(int i = 0; i < M; i++)
{
scanf("%d %d %d", &u, &v, &c);
edges[i].from = u, edges[i].to = v, edges[i].cost = c;
}
sort(edges, edges + M);
Ans.clear();
for(int i = 0; i < Q; i++)
{
scanf("%d", &Query[i]);
qq[i] = Query[i];
}
sort(qq, qq + Q);
ll s = 0;
for(int i = 1; i <= N; i++) pa[i] = i, num[i] = 1;
int j = 0;
for(int i = 0; i < M; i++)
{
int fa = Find(edges[i].from), fb = Find(edges[i].to);
while(j < Q && edges[i].cost > qq[j])
{
Ans[qq[j]] = s;
j++;
}
if(fa != fb)
{
s = (ll)s - num[fa] * (num[fa] - 1) - num[fb] * (num[fb] - 1) + (num[fa] + num[fb]) * (num[fa] + num[fb] - 1);
pa[fb] = fa;
num[fa] += num[fb];
}
}
while(j < Q) Ans[qq[j++]] = s;
for(int i = 0; i < Q; i++)
printf("%I64d\n", Ans[Query[i]]);
}
return 0;
}