HDU 3938 离线并查集+二分

思路:对于一个能量L ,如果边权<=L  ,则将两点放在同一集合内,所有的边都考虑了以后,
设ai为第i个集合的顶点个数,则结果为sigma(ai*(ai-1)/2);但是直接这样做会超时。

所以采用二分的做法。对边按权值从小到大排序,在加边的过程中不断更新边的最大值,并记录结果。

每次加边,检查边的两个顶点是否在同一集合内,如果不在同一集合内,设两个集合的顶点数为A1和A2,结果+=A1×A2。


 #include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<iostream>

#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn = 1e4 + 5;
const int maxm = 5e5 + 5;

int n,m,q;
int par[maxn];
int num[maxn];

struct edge{
    int u,v,cost;
    bool operator < (const edge& z){
        return cost < z.cost;
    }
}e[maxm];
int cost[maxm],ans[maxm];

void init(){
    CLR(ans,0);
    for(int i=0;i<maxn;i++){
        par[i] = -1;
        num[i] = 1;
    }
}

int find(int x){
    while(par[x] >= 0) x = par[x];
    return x;
}

void unite(int x,int y){
    x = find(x);y = find(y);
    if(x == y) return;
    par[y] = x;
    num[x] += num[y];
}

bool same(int x,int y){
    return find(x) == find(y);
}

void build(){
    int j = 0,rec = e[0].cost;
    for(int i=0;i<m;i++){
        if(rec > e[i].cost){
            int u = find(e[i].u),v = find(e[i].v);
            if(!same(u,v)){
                ans[j] += num[u]*num[v];
                unite(u,v);
            }
        }else{
            //printf("111111\n");
            int te = j;
            j = i;rec = e[j].cost;
            int u = find(e[i].u),v = find(e[i].v);
            if(!same(u,v)){
                ans[j] = ans[te] + num[u]*num[v];
                unite(u,v);
            }
            else ans[j] = ans[te];
        }
        //cout<<j<<" "<<i<<" "<<ans[j]<<" "<<e[i].cost<<" "<<rec<<endl;
    }
    /*for(int i=0;i<m;i++){
        cout<<e[i].cost<<" ";
    }
    cout<<endl;
    for(int i=0;i<m;i++){
        cout<<ans[i]<<" ";
    }
    cout<<endl;*/
}


int main(){
    while(~scanf("%d%d%d",&n,&m,&q)){
        init();
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].cost);
            cost[i] = e[i].cost;
        }
        sort(cost,cost+m);
        sort(e,e+m);
        build();
        int l;
        while(q--){
            scanf("%d",&l);
            int index = upper_bound(cost,cost+m,l) - cost - 1;
            printf("%d\n",ans[index]);
        }

    }

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值