HDU 5441
题意:
杰克喜欢旅游,从城市a到城市b是他最喜欢的,现在有n个城市m个路。
当杰克在从城市a到城市b 的时候需要坐车,但是需要有等待时间t,他无法忍受超过x 的等待时间。
问:给出不同的x的忍耐时间,最多有多少a->b的方式,a->b和b->a当做两种不同的方式。
思路:
由于x值有很多个,不能每次查找的时候都要新建立一个图,为了利用上一步的运算,不妨对询问先从小到大排序,对所有的边先保存在结构体中。
按照询问的x值,依次对图中加入边,由于问题是有多少a->b 的方式,所以可以不用保存图是怎么建立的,只需要知道不同的连通图有多少个不同的点即可,接着可以想到用并查集维护每一个连通图,顺便记录每一个联通图中点的数量,然后直接通过组合数可以计算每一个联通图的a->b.
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct kruskal
{
int a,b,value;
}edge[100005];
int f[20005];
int s[20005];
int Find(int x)
{
if(f[x] == x)
return x;
else
f[x] = Find(f[x]);
return f[x];
}
int Union(int x,int y)
{
int a = Find(x);
int b = Find(y);
if(a == b)
return 0;
else if(s[a] <= s[b]){
f[a] = b;
s[b] += s[a];
}
else {
f[b] = a;
s[a] += s[b];
}
return 1;
}
bool cmp1(kruskal a,kruskal b)
{
return a.value < b.value;
}
struct Q
{
int id;
int v;
long long ans;
}qq[5005];
bool cmp2(Q a, Q b)
{
return a.v < b.v;
}
bool cmp3(Q a, Q b)
{
return a.id < b.id;
}
int n,m,q;
int main()
{
// freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--) {
scanf("%d%d%d",&n,&m,&q);
for(int i = 1;i <= m; i++) {
int u,t,v;
scanf("%d%d%d",&u,&t,&v);
edge[i].a = u,edge[i].b = t,edge[i].value = v;
}
for(int i = 1;i <= q; i++) {
qq[i].id = i;
int temp;
scanf("%d",&temp);
qq[i].v = temp;
qq[i].ans = 0;
}
sort(edge+1,edge+1+m,cmp1);
sort(qq+1,qq+1+q,cmp2);
for(int i = 1;i <= n; i++) {
f[i] = i;
s[i] = 1;
}
int pos = 1;
long long ans = 0;
for(int i = 1;i <= q; i++) {
for(;pos <= m && edge[pos].value <= qq[i].v;pos++) {
if(edge[pos].a == edge[pos].b) continue;
int xx = Find(edge[pos].a);
int yy = Find(edge[pos].b);
if(xx != yy) {
ans -= (long long)s[xx]*(s[xx]-1);
ans -= (long long)s[yy]*(s[yy]-1);
Union(xx,yy);
ans += (long long)s[Find(xx)]*(s[Find(xx)]-1);
}
}
qq[i].ans = ans;
}
sort(qq+1,qq+1+q,cmp3);
for(int i = 1;i <= q; i++) {
printf("%lld\n",qq[i].ans);
}
}
return 0;
}