题意:
有n个城市,m条道路,每条道路都有一个建造费用
只能建成建造费用<=k的道路
已知k,求能互相连通的城市最多有多少对
就是说求把<=k的路都建好了之后,每个集合的互相连通的点数之和
一个集合中互相连通的点数 = p*(p-1)/2 其中p为集合中点的个数 ,这样的操作次数就是n*m,显然不行
当两个不相连的城市团A,B合并时,会产生sizeA * sizeB个互相连通的城市,sizeAB表示集合中城市的个数
即每加入一条边就会增加sizeA * sizeB个互相连通的城市(当AB不在同一个集合中时
ans[i] 记录加入排序后的第i条边后互相连通的城市个数
有两种情况
1、ans[i] = ans[i-1] + sizeA * sizeB (连接了两组不同的城市)
2、ans[i] = ans[i-1] (两个城市已经连接)
然后存一下就行了
找的时候二分找到第一个大于k的,然后取它的下一个答案就行了
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n,m,q,k;
int fa[N],ans[N];
int cost[N];
struct node{
int u,v,cost;
}e[N];
bool cmp(node a,node b){
return a.cost<b.cost;
}
int find(int x){
return fa[x]==x ? x : fa[x] = find(fa[x]);
}
int cnt[N],now = 1;
void slove(){
sort(e+1,e+1+m,cmp);
for(int i=0;i<=n;i++) {fa[i] = i; cnt[i] = 1;}
for(int i=1;i<=m;i++){
cost[i] = e[i].cost;
if(e[i].cost>=N) break;
int faa = find(e[i].u);
int fbb = find(e[i].v);
ans[i] = ans[i-1];
if(faa == fbb) continue;
fa[fbb] = faa;
ans[i] += cnt[faa]*cnt[fbb];
cnt[faa] += cnt[fbb];
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].cost);
}
slove();
scanf("%d",&q);
while(q--){
scanf("%d",&k);
if(k>=e[m].cost) printf("%d\n",ans[m]);
else{
int pos = upper_bound(cost+1,cost+1+m,k) - cost;
printf("%d\n",ans[pos-1]);
}
}
return 0;
}