题意:一张无向图,求有多少条路径使得路径上的花费小于L,这里路径上的花费是这样规定的,a、b两点之间的多条路径中的最长的边最小值。
分析:并查集+离线处理。按最小生成树的思想,按边权值由小到大排。对于询问按问的l由小到大排,最后复原回去。当边权值小于询问值就合并边的两端点。用r[k]记录树的节点数。两颗树合并成一棵树时, 将会增加r[a]*r[b]条路径。
#include<stdio.h>
#include<algorithm>
#define N 10005
#define M 50005
using namespace std;
int par[N];//父亲
int r[N];//树的高度
int V,E;
int n,m,q;
struct edge
{
int u,v,w;
}es[M];
struct node
{
int id;
int l;
int ans;
node(int id=0,int l=0,int ans=0):id(id),l(l),ans(ans){}
}que[N];
bool comp1(const edge a,const edge b)
{
return a.w<b.w;
}
bool comp2(const node a,const node b)
{
return a.l<b.l;
}
bool comp3(const node a,const node b)
{
return a.id<b.id;
}
void init(int n)//初始化n个元素
{
for(int i=0;i<=n;i++)
{
par[i]=i;
r[i]=1;
}
return ;
}
int find(int x)//查询树的根
{
if(par[x]==x)
return x;
else
return par[x]=find(par[x]);
}
int unite(int x,int y)//合并x和y所属的集合
{
int cnt=0;
x=find(x);
y=find(y);
if(x==y)
return 0;
cnt=r[x]*r[y];
if(r[x]<r[y])
{
par[x]=y;
r[y]+=r[x];
}
else
{
par[y]=x;
r[x]+=r[y];
}
return cnt;
}
bool same(int x,int y)//判断x和y是否属于同一个集合
{
return find(x)==find(y);
}
void kruskal(void)
{
sort(es,es+E,comp1);
sort(que,que+q,comp2);
init(V);
int j=0;
for(int i=0;i<N;i++)
{
while(j<E&&es[j].w<=que[i].l)
{
if(same(es[j].u,es[j].v))
{
j++;
continue;
}
else
{
que[i].ans+=unite(es[j].u,es[j].v);
j++;
}
}
if(i>=1)
que[i].ans+=que[i-1].ans;
}
return ;
}
int main(void)
{
while(scanf("%d%d%d",&n,&m,&q)==3)
{
for(int i=0;i<m;i++)
scanf("%d%d%d",&es[i].u,&es[i].v,&es[i].w);
for(int i=0; i<q; i++)
{
scanf("%d",&que[i].l);
que[i].id=i;
que[i].ans=0;
}
V=n;
E=m;
kruskal();
sort(que,que+q,comp3);
for(int i=0; i<q; i++)
{
printf("%d\n",que[i].ans);
}
}
return 0;
}