(最小生成树(并查集加边法))2015网选,hdu5441 Travel


/*
题目:1005.Travel
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5441
题目大意:给出n个顶点、m条边、k个x值,每条边有相应的权值;对每个x值,求只借助权值<=x的边而连通的顶点对有多少对?
分析:最小生成树(并查集加边法)。
    只需求一遍最小生成树,O(m)。
    因为求解答案时,处理较大的x可以利用处理完较小的x留下的图(最小生成树的零碎部分),故将边按权值排序依次进行加边,将x从小到大排序依次处理。
    对于当前x,加边加到权值>x的时候就不加了;处理下一个更大的x时,再继续在上一次停下的位置继续加。
        答案利用求最小生成树的中间结果解。利用并查集记录以结点v为代表的结点集合的结点个数,则每加一条边{x,y},ans = ans - (root[x] * (root[x] - 1) + root[y] * (root[y] - 1)) + root[xy] * (root[xy] - 1);
 
优化:当已经得到一棵最小生成树时,便任意两点均连通,剩下的边都可以不处理。
ps:只用并查集也可以,貌似用最小生成树的好处就只有那个优化。
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <algorithm>
using namespace std;
#define mem(x,i)   memset(x,i,sizeof(x))
#define sfi(a)     scanf("%d", &a)  
#define sfii(a,b)  scanf("%d %d", &a, &b)  
#define sfiii(a,b,c)scanf("%d %d %d", &a, &b, &c)  
const double EPS = 1e-10;
const double pai = acos(-1.0);
const int INF = 0xfffffff;
const int MOD = 1000000007;
typedef long long LL;
const int maxq = 5000 + 5;
struct Edge{
    int u, v, w;
    Edge(int u, int v, int w) :u(u), v(v), w(w){}
    bool operator < (const Edge& e)const{
        return w < e.w;
    }
};
const int max_node = 20000 + 5;
int root[max_node];
struct UFS{
    int S[max_node];
    void init(int n){
        for (int i = 1; i <= n; i++){
            S[i] = i;
            root[i] = 1;
        }
    }
    int findset(int x){
        return S[x] == x ? x : S[x] = findset(S[x]);
    }
    int merge(int x, int y){
        return S[x] = y;
    }
}ufs;
struct MST{
    bool Kruskal(int &cnt, vector<Edge> &e,int &st, int x,int &ret){
        if (cnt <= 1)return true;
        for (int i = st; i < e.size(); i++){
            if (e[i].w>x) return false;
            st = i;
 
            int x = ufs.findset(e[i].u);
            int y = ufs.findset(e[i].v);
            if (x != y){
                ret -= (root[x] * (root[x] - 1) + root[y] * (root[y] - 1));
                int xy = ufs.merge(x, y);
                root[xy] = root[x] + root[y];
                ret += root[xy] * (root[xy] - 1);
                if (--cnt == 1)return true;
            }
        }
        return false;
    }
}mst;
struct NODE{
    int no, x;
    bool operator <(const NODE &b)const{
        return x < b.x;
    }
    NODE(int x, int no) :x(x), no(no){}
};
vector<Edge> e;
vector<NODE> x;
int ans[maxq];
int N, M, Q, X;
int main()
{
    //freopen("f:\\input.txt", "r", stdin);
    int T;
    sfi(T);
    while (T--){
        sfiii(N, M, Q);
        e.clear();
        x.clear();
        ufs.init(N);
        for (int i = 0; i < M; i++){
            int u, v, w;
            sfiii(u, v, w);
            e.push_back(Edge(u, v, w));
        }
        sort(e.begin(), e.end());
        for (int i = 1; i <= Q; i++){
            sfi(X);
            x.push_back(NODE(X, i));
        }
        sort(x.begin(), x.end());
 
        int st = 0, ret = 0, cnt = N;
        for (int i = 0; i < x.size(); i++){
            if (!mst.Kruskal(cnt, e, st, x[i].x, ret))
                ans[x[i].no] = ret;
            else
                ans[x[i].no] = N*(N - 1);
        }
        for (int i = 1; i <= Q; i++){
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值