HDU 5441并查集 by cyl

HDU 5441

题意:

​ 杰克喜欢旅游,从城市a到城市b是他最喜欢的,现在有n个城市m个路。

​ 当杰克在从城市a到城市b 的时候需要坐车,但是需要有等待时间t,他无法忍受超过x 的等待时间。

​ 问:给出不同的x的忍耐时间,最多有多少a->b的方式,a->b和b->a当做两种不同的方式。

思路:

​ 由于x值有很多个,不能每次查找的时候都要新建立一个图,为了利用上一步的运算,不妨对询问先从小到大排序,对所有的边先保存在结构体中。

​ 按照询问的x值,依次对图中加入边,由于问题是有多少a->b 的方式,所以可以不用保存图是怎么建立的,只需要知道不同的连通图有多少个不同的点即可,接着可以想到用并查集维护每一个连通图,顺便记录每一个联通图中点的数量,然后直接通过组合数可以计算每一个联通图的a->b.

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;

struct kruskal
{
    int a,b,value;
}edge[100005];

int f[20005];
int s[20005];

int Find(int x)
{
    if(f[x] == x)
        return x;
    else
        f[x] = Find(f[x]);
    return f[x];
}

int Union(int x,int y)
{
    int a = Find(x);
    int b = Find(y);
    if(a == b)
        return 0;
    else if(s[a] <= s[b]){
        f[a] = b;
        s[b] += s[a];
    }
    else {
        f[b] = a;
        s[a] += s[b];
    }
    return 1;
}

bool cmp1(kruskal a,kruskal b)
{
    return a.value < b.value;
}

struct Q
{
    int id;
    int v;
    long long ans;
}qq[5005];

bool cmp2(Q a, Q b)
{
    return a.v < b.v;
}
bool cmp3(Q a, Q b)
{
    return a.id < b.id;
}

int n,m,q;
int main()
{
//    freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d%d",&n,&m,&q);
        for(int i = 1;i <= m; i++) {
            int u,t,v;
            scanf("%d%d%d",&u,&t,&v);
            edge[i].a = u,edge[i].b = t,edge[i].value = v;
        }
        for(int i = 1;i <= q; i++) {
            qq[i].id = i;
            int temp;
            scanf("%d",&temp);
            qq[i].v = temp;
            qq[i].ans = 0;
        }
        sort(edge+1,edge+1+m,cmp1);
        sort(qq+1,qq+1+q,cmp2);
        for(int i = 1;i <= n; i++) {
            f[i] = i;
            s[i] = 1;
        }
        int pos = 1;
        long long ans = 0;
        for(int i = 1;i <= q; i++) {
            for(;pos <= m && edge[pos].value <= qq[i].v;pos++) {
                if(edge[pos].a == edge[pos].b) continue;
                int xx = Find(edge[pos].a);
                int yy = Find(edge[pos].b);
                if(xx != yy) {
                    ans -= (long long)s[xx]*(s[xx]-1);
                    ans -= (long long)s[yy]*(s[yy]-1);
                    Union(xx,yy);
                    ans += (long long)s[Find(xx)]*(s[Find(xx)]-1);
                }
            }
            qq[i].ans = ans;
        }
        sort(qq+1,qq+1+q,cmp3);
        for(int i = 1;i <= q; i++) {
            printf("%lld\n",qq[i].ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值