题意:在一张无向图上,已知边权,做q组询问,问小于L的点对共有几组。点对间的距离取=min(两点之间每一条通路上的最大值)。
分析:这里取最大值的最小值,常用到二分。而这里利用离线算法,先对边从小到大排序,逐一加入集合中。利用并查集,当两点之间不在同一个集合,那么所加入的边就是两个集合中任一点对的距离(两集合各取一点)。所以有cnt2+=num[fu]*num[fv];
注意:有些询问比m条边中的最小边还小,比最大边还大。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int MAXN=; struct Edge{
int u,v,c;
}edge[*MAXN]; struct Query{
int sq,id;
}query[MAXN]; int num[MAXN],ans[MAXN];
int f[MAXN]; int cmp1(Edge a,Edge b)
{
return a.c<b.c;
} int cmp2(Query a,Query b)
{
return a.sq<b.sq;
} void init(int n)
{
for(int i=;i<=n;i++)
{
num[i]=;
f[i]=i;
}
} int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
} int main()
{
int n,m,q;
while(~scanf("%d%d%d",&n,&m,&q))
{
for(int i=;i<m;i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].c);
sort(edge,edge+m,cmp1); for(int i=;i<q;i++)
{
scanf("%d",&query[i].sq);
query[i].id=i;
}
sort(query,query+q,cmp2); init(n);
int cnt1,cnt2,tol=;
cnt1=cnt2=;
for(int i=;i<m;i++)
{
int u=edge[i].u;
int v=edge[i].v;
int c=edge[i].c; int fu=find(u);
int fv=find(v); if(fu==fv)//在同一集合不影响
continue; cnt1=cnt2;//记录上一个值
cnt2+=num[fu]*num[fv]; f[fv]=fu;
num[fu]+=num[fv];//注意这两步处理要相同 while(c>query[tol].sq)//取cnt1更新询问
{
ans[query[tol].id]=cnt1;
tol++;
}
}
while(tol<q)//把大于最大边的询问统一处理成cnt2
{
ans[query[tol].id]=cnt2;
tol++;
}
for(int i=;i<q;i++)
printf("%d\n",ans[i]);
}
return ;
}