题目
Sample input
1
5 5 3
2 3 6334
1 5 15724
3 5 5705
4 3 12382
1 3 21726
6000
10000
13000
Sample output
2
6
12
解
直接每读入一次询问便建一遍图所用时间过于长…
遂考虑将边和提问都从小到大排序,对于一个提问:连上权值小于它的边,然后用上并查集判断两点是否连通即可。
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[200020],sl[200020],Ans[50050];
int n,m,q,t;
struct asdf{
int u,v,z;
} Q[1000005];
struct sdfg{
int bh,zz;
} Ask[50050];
bool cmp1(asdf aa,asdf bb){ //排序用
return aa.z<bb.z;
}
bool cmp2(sdfg aa,sdfg bb){
return aa.zz<bb.zz;
}
int find(int d){ //找父亲
if(f[d] == d) return d;
else return f[d] = find(f[d]);
}
int init(){ //输入
t = 0;
scanf("%d%d%d",&n,&m,&q);
for(int i = 1; i <= n; ++i){ //初始化
f[i] = i; //父亲
sl[i] = 1; //以i为根的并查集的大小
}
for(int i = 1; i <= m; ++i){
int uu,vv,zzz;
scanf("%d%d%d",&uu,&vv,&zzz);
Q[++t] = {uu,vv,zzz};
}
for(int i = 1; i <= q; ++i){
scanf("%d",&Ask[i].zz);
Ask[i].bh = i;
}
sort(Q+1,Q+1+t,cmp1); //排序
sort(Ask+1,Ask+1+q,cmp2);
int h = 1, lans = 0;
for(int i = 1; i <= q; ++i){ //询问
while(Q[h].z <= Ask[i].zz){
if(find(Q[h].u)!=find(Q[h].v)){ //还没连上
lans += sl[find(Q[h].u)] * sl[find(Q[h].v)]; //新增点的对数
sl[find(Q[h].v)] += sl[find(Q[h].u)]; //并查集大小
f[find(Q[h].u)] = find(Q[h].v);
}
++h;
}
Ans[Ask[i].bh] = lans; //还原询问顺序
}
for(int i = 1; i <= q; ++i) //输出
printf("%d\n",Ans[i] * 2); //因为a-b和b-a被视为两条路径
}
int main(){
int test;
scanf("%d",&test);
while(test--){
init();
}
}