思路:对于一个能量L ,如果边权<=L ,则将两点放在同一集合内,所有的边都考虑了以后,
设ai为第i个集合的顶点个数,则结果为sigma(ai*(ai-1)/2);但是直接这样做会超时。
所以采用二分的做法。对边按权值从小到大排序,在加边的过程中不断更新边的最大值,并记录结果。
设ai为第i个集合的顶点个数,则结果为sigma(ai*(ai-1)/2);但是直接这样做会超时。
所以采用二分的做法。对边按权值从小到大排序,在加边的过程中不断更新边的最大值,并记录结果。
每次加边,检查边的两个顶点是否在同一集合内,如果不在同一集合内,设两个集合的顶点数为A1和A2,结果+=A1×A2。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iostream>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn = 1e4 + 5;
const int maxm = 5e5 + 5;
int n,m,q;
int par[maxn];
int num[maxn];
struct edge{
int u,v,cost;
bool operator < (const edge& z){
return cost < z.cost;
}
}e[maxm];
int cost[maxm],ans[maxm];
void init(){
CLR(ans,0);
for(int i=0;i<maxn;i++){
par[i] = -1;
num[i] = 1;
}
}
int find(int x){
while(par[x] >= 0) x = par[x];
return x;
}
void unite(int x,int y){
x = find(x);y = find(y);
if(x == y) return;
par[y] = x;
num[x] += num[y];
}
bool same(int x,int y){
return find(x) == find(y);
}
void build(){
int j = 0,rec = e[0].cost;
for(int i=0;i<m;i++){
if(rec > e[i].cost){
int u = find(e[i].u),v = find(e[i].v);
if(!same(u,v)){
ans[j] += num[u]*num[v];
unite(u,v);
}
}else{
//printf("111111\n");
int te = j;
j = i;rec = e[j].cost;
int u = find(e[i].u),v = find(e[i].v);
if(!same(u,v)){
ans[j] = ans[te] + num[u]*num[v];
unite(u,v);
}
else ans[j] = ans[te];
}
//cout<<j<<" "<<i<<" "<<ans[j]<<" "<<e[i].cost<<" "<<rec<<endl;
}
/*for(int i=0;i<m;i++){
cout<<e[i].cost<<" ";
}
cout<<endl;
for(int i=0;i<m;i++){
cout<<ans[i]<<" ";
}
cout<<endl;*/
}
int main(){
while(~scanf("%d%d%d",&n,&m,&q)){
init();
for(int i=0;i<m;i++){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].cost);
cost[i] = e[i].cost;
}
sort(cost,cost+m);
sort(e,e+m);
build();
int l;
while(q--){
scanf("%d",&l);
int index = upper_bound(cost,cost+m,l) - cost - 1;
printf("%d\n",ans[index]);
}
}
return 0;
}