https://vjudge.net/contest/166649#problem/E
第一次了解并查集是在krusal求最小生成树,排序边,每次添加最小的并且不能在一个联通快上的边,就用到了他,因为判断是否为一个连通度用常规的写法是很耗费的。而并查集就很轻松。
注意每次有两个连通度变成了一个,就要把他们的值想成并且乘2,这样就是他们相互到达的次数。并且结果可以保存,
因为已经离线化按查询从小到大,所以小的结果可以运用,并且保存了查询的位置,如果某一结果已经到达了终点,那么以后都不用再比较。不然会错。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include <vector>
#include <queue>
#include <map>
const int maxn=2e5+10;
using namespace std;
struct Node{
int fir;
int sec;
long long v;
}node[maxn];
bool cmp2(Node a,Node b){
return a.v<b.v;
}
int pre[maxn];
int cnt[maxn];
pair<int,int> query[maxn];
bool cmp3(pair<int,int> a,pair<int,int >b){
return a.first<b.first;
}
int find1(int x){
if(pre[x]==x) return x;
return pre[x]=find1(pre[x]);
}
int main()
{
int t;
int a,b;
long long f;
int x,y;
long long c;
int p;
scanf("%d",&t);
while(t--){
scanf("%d%d%lld",&a,&b,&p);
for(int i=1;i<=b;i++){
scanf("%d%d%lld",&x,&y,&c);
node[i].fir=x;
node[i].sec=y;
node[i].v=c;
pre[i]=i,cnt[i]=1;
}
for(int j=1;j<=p;j++){
scanf("%d",&query[j].first);
query[j].second=j;
}
sort(query+1,query+p+1,cmp3);
sort(node+1,node+b+1,cmp2);
int ans=0;
int pos=1;
int qqq[maxn];
int i;
for(int j=1;j<=p;j++){
ans=1;
for( i=pos;i<=b;i++){
if(node[i].v>query[j].first){
pos=i;
break;
}
else{
int a1=find1(node[i].fir);
int b1=find1(node[i].sec);
if(a1==b1) continue;
else
pre[b1]=a1;
ans=2*cnt[a1]*cnt[b1];
cnt[a1]+=cnt[b1];
}
}
qqq[query[j].second]=ans;
pos=i;
}
for(int i=1;i<=p;i++){
printf("%d\n",qqq[i]);
}
}
return 0;
}