题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=5441
题意:给定一些城市,城市间带距离的路连接,一个懒人要去旅游,带不想走太远,给定一个最大忍耐距离,要求他去的城市间不能超过这个距离,a-->b算一条,反过来也算,如果3个城市联通的,那他可以去的城市为3*2=6,不联通的不算一起,分开算相加
题解:先根据输入的查询和边距进行排序,从小到大,对于边一条条遍历,用并查集维护,记录值,具体见代码注释
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
map<int,int> mp; //用于记录并查集中的根的位置
int numz;
typedef struct
{
int start,ed,d;
}Edge;
Edge edge[100500];
bool compare(Edge a,Edge b)
{
return a.d<b.d;
}
typedef struct
{
int q;
int condition;
}Query;
Query query[5050];
bool compareq(Query q1,Query q2)
{
return q1.q<q2.q;
}
int vis[20500];//用于记录是否访问
int num[20500];//用于记录联通的城市数
int head[20500];//用于记录联通块的头
long long ans[5050];//记录结果
int findf(int x)//并查集
{
if(vis[x]==x) return x;
else
{
vis[x]=findf(vis[x]);
return vis[x];
}
}
void init(int x,int y)//并查集
{
int d1=findf(x);
int d2=findf(y);
if(d1!=d2)
{
if(num[d1]!=1) head[mp[d1]]=0; //若d1为一大群点的根,要去掉头,标记为0
if(num[d2]==1) {head[numz]=d2;mp[d2]=numz++;} //若d2为单点,则要进行头的存放
vis[d1]=d2;num[d2]+=num[d1];
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,q;
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&edge[i].start,&edge[i].ed,&edge[i].d);
}
sort(edge+1,edge+m+1,compare);
for(int i=1;i<=q;i++)
{
scanf("%d",&query[i].q);
query[i].condition=i;
}
sort(query+1,query+q+1,compareq);
int cnt=1;//记录查询
numz=1;//距离有多少超过1的联通块
for(int i=1;i<=n;i++) {vis[i]=i;num[i]=1;}//并查集初始化
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
while(edge[i].d>query[cnt].q && cnt<=q)//当刚好大于查询的距离时进行赋值
{
long long sum=0;
for(int j=1;j<numz;j++)
{
if(!head[j]) continue;
sum+=num[head[j]]*(num[head[j]]-1);
}
ans[query[cnt].condition]=sum;
cnt++;
}
init(edge[i].start,edge[i].ed);
}
if(cnt<=q)//防止查询距离大于边最大距离的情况
{
long long sum=0;
for(int j=1;j<numz;j++)
{
if(!head[j]) continue;
sum+=num[head[j]]*(num[head[j]]-1);
}
for(int j=cnt;j<=q;j++)
{
ans[query[j].condition]=sum;
}
}
for(int i=1;i<=q;i++)
{
printf("%lld\n",ans[i]);
}
mp.clear();
}
return 0;
}