【并查集】旅游

题目

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();
 }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值