给你力量L,求有多少条path的力量小于等于L。
这个path消耗的力量是T,T是U到V上最长的边。
所以说,只要求得有多少个点对使得点对之间的最大的边小于L即可。
采用并查集,离线计算,询问从小到大排序,然后边从小到大排序,然后一个一个加入并查集中,加一条,计算一次两个集合的点的数目的乘积即可。
#include <iostream>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <utility>
#define inf (1<<28)
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int N=10010;
const int M=50002;
struct Node {
int u,v,len;
}edge[M];
int cmp(Node a,Node b){return a.len<=b.len;}
int fa[N],sum[N];
int n,m,q;
void init()
{
for(int i=1;i<=n;i++){fa[i]=i;sum[i]=1;}
}
int findx(int x)
{
return fa[x]=fa[x]==x?x:findx(fa[x]);
}
int Union(int a,int b){
int aa=findx(a);
int bb=findx(b);
//if(aa==bb)return 0;
if(fa[aa]<fa[bb])
{
fa[bb]=aa;
int tmp=sum[aa]*sum[bb];
sum[aa]+=sum[bb];
//cout<<" ____"<<tmp<<endl;
return tmp;
}
else {
fa[aa]=bb;
int tmp=sum[aa]*sum[bb];
sum[bb]+=sum[aa];
//cout<<" ____"<<tmp<<endl;
return tmp;
}
}
int ans[N];
struct Q{
int L,id,ans;
}que[N];
bool cmp1(Q a,Q b){return a.L<=b.L;}
bool cmp2(Q a,Q b){return a.id<b.id;}
int main()
{
while(scanf("%d%d%d",&n,&m,&q)!=EOF){
init();
for(int i=0;i<m;i++)scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].len);
for(int i=0; i<q; i++)
{scanf("%d",&que[i].L);que[i].id=i;que[i].ans=0;}
sort(edge,edge+m,cmp);
sort(que,que+q,cmp1);
int cnt=0;
for(int i=0;i<q;i++){
while(edge[cnt].len<=que[i].L&&cnt<m)
{
int aa=findx(edge[cnt].u);
int bb=findx(edge[cnt].v);
if(aa==bb)
{
cnt++;
continue;
}
else{
//cout<<"------------"<<sum[aa]<<" "<<sum[bb]<<endl;
que[i].ans+=Union(edge[cnt].u,edge[cnt].v);cnt++;
//cout<<Union(edge[i].u,edge[i].v)<<endl;
}
}
if(i>=1)que[i].ans+=que[i-1].ans;//这里之前放在循环里面调了好久。。。
}
sort(que,que+q,cmp2);//还原之前的序列
for(int i=0; i<q; i++)
{
printf("%d\n",que[i].ans);
}
}
return 0;
}