并查集(hdu 5441)

本文介绍了一种使用并查集数据结构解决旅行路径问题的方法。问题要求在一个由多个城市组成的网络中,找到所有可达城市的对数,使得任意两个城市的距离不超过旅行者能接受的最大忍耐距离。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值