HDU5441 Travel (2015年長春網路賽,並查集)

bigbigship發表於2015-09-14

題目連結:傳送門 

題意:

一個無向圖有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;
}




相關文章