HDU5441 Travel (2015年长春网络赛,并查集)

题目链接:传送门 

题意:

一个无向图有n个节点m条边,每条边都有一个权值,然后有Q次询问,每次询问给定一个阀值,表示一个人从一个节点去另外一个节点经过的每条边的权值都不能超过这个阀值,那么这样的节点对数有多少个,(u,v)与(v,u)看作不同的。


分析:

模拟贪心法求最小生成树的过程,将所有的边按权值从小到大排序,所有的询问也按照阀值的从小到大排序,然后再边权小于阀值的时候就不断加边,然后用并查集维护一下所有联通快的数量,如果两个联通快互相不连通的话那么当他们连起来的时候所做的贡献为,他们相同后的点对数减去他们各自的点对数。


代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

const int maxn = 2e4+10;

struct Edge{
    int u,v,w;
    bool operator <(const struct Edge &tmp)const{
        return this->w<tmp.w;
    }
}edg[maxn*5];

struct query{
    int val,id;
    bool operator <(const struct query &tmp)const{
        return this->val<tmp.val;
    }
}Q[maxn/4];

struct UFS{
    int par[maxn],num[maxn];
    void init(){
        for(int i=0;i<maxn;i++){
            par[i]=i;
            num[i]=1;
        }
    }
    int find_par(int x){
        if(x!=par[x]) return par[x]=find_par(par[x]);
        return par[x];
    }
    void Union(int x,int y,int &sum){
        x=find_par(x);
        y=find_par(y);
        if(y<x) swap(x,y);
        if(x!=y){
            par[y]=x;
            sum+=(num[x]+num[y])*(num[x]+num[y]-1) - num[x]*(num[x]-1) - num[y]*(num[y]-1);
            num[x]+=num[y];
        }
    }
}T;

int ans[maxn/4];

int main()
{
    int t,n,m,q;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&q);
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w);
        }
        sort(edg,edg+m);
        for(int i=0;i<q;i++){
            scanf("%d",&Q[i].val);
            Q[i].id=i;
        }
        sort(Q,Q+q);
        memset(ans,0,sizeof(ans));
        T.init();
        int sum=0;
        for(int i=0,j=0;i<q;i++){
            while(edg[j].w<=Q[i].val&&j<m){
                T.Union(edg[j].u,edg[j].v,sum);
                j++;
            }
            ans[Q[i].id]=sum;
        }
        for(int i=0;i<q;i++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值