题意:南京理工大学有很多景点。两个景点组成一对。两个景点的价值用这种方式计算:设两个景点之间一条路径中最长的一条路的距离是s,这两个景点的价值f就是所有路径中s的最小值。游客想要找价值大于等于t的景点对有多少个。
思路:将边按照权值从小到大排序。依次遍历每条边。因为每次加入的边是当前遍历过的边中最大的,若这条边连接的两个节点不在同一个集合中,那么这两个集合之间点对的价值就是这条边的权值。
错误:合并的时候把两个集合大小计算写错了。。不是一次犯这种低级错误了。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<map>
#include<set>
#include<list>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<time.h>
#include<iomanip>
#include<assert.h>
using namespace std;
typedef long long ll;
struct Edge
{
int u,v,w;
bool operator < (const Edge &a) const {
return w < a.w;
}
bool operator == (const Edge &a) const {
return w == a.w;
}
bool operator > (const Edge &a) const {
return w>a.w;
}
Edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
}edge[500005];
int fa[10005];
int cnt[10005];
int find(int x)
{
return fa[x] = fa[x] == x? fa[x] : find(fa[x]);
}
void merge(int x,int y)
{
int a = find(x);int b = find(y);
fa[a] = b;
cnt[b] += cnt[a];
cnt[a] += cnt[b];
}
int n,m;
ll ans[500005];
int bisearch(int x){
int l = 0;int r = m-1;
while(l<=r){
int mid = (l+r);mid>>=1;
if(edge[mid].w == x){
return mid;
}
if(edge[mid].w<x){
l = mid+1;
} else r = mid-1;
}
return l;
}
int main()
{
// freopen("data.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
for(int i = 0 ; i < m; ++ i ){
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge,edge+m);
for(int i = 0; i < n;++i){
fa[i] = i;
cnt[i] = 1;
}
memset(ans,0,sizeof(ans));
for(int i = 0 ; i < m; ++i){
int u = edge[i].u;
int v = edge[i].v;
int a = find(u);int b = find(v);
if(a == b)continue;
ans[i] = (ll)cnt[a]*(ll)cnt[b]*2;
merge(a,b);
}
for(int i = m-2;i>=0;--i){
ans[i] += ans[i+1];
}
int p;
scanf("%d",&p);
while(p--){
int t;
scanf("%d",&t);
int c = bisearch(t);
printf("%I64d\n",ans[c]);
}
}
return 0;
}