HDU 7123 City

7123

题意:给出一个带权非联通图,图中有若干联通分量,图中的点代表城市,有一些城市可以互相到达。试去掉边权小于x的所有边,试问去掉这些边后,还有多少对城市可以互相到达?

并查集+离散化

思路:
可以先对每个连通分量进行预处理,即保留权值尽可能大的边使得连通分量保持原有连通性,对于连通分量中一些无用(权值较小)的边直接删除。优化连通分量的具体操作是对按照权值进行排序,优先选用权值较大的边,这样总能生成一颗更健壮的树。建树的同时记录每种权值的边所能连接的点对数,这样破坏某些边时就能快速获取这些边能影响的点对数。最后对记录做一下前缀预处理,方便统计某种前缀。
场上写的时候,数组初始化没整好,WA了好几次。

code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10 ;

struct Node{
    int x , y , w;
    bool operator < (Node no1) const{
        return w > no1.w ;
    }
};
vector<int>S;
set<int>st ;
int t , n , m , Q ;
int q[N] ;
int p[N] ;
Node Eg[N] ;
int cnt[N] ;
int Cc[N*2];
int All = 0 ;
int get(int x)
{
    return lower_bound(S.begin(),S.end(),x) - S.begin() + 1;
}

int fr(int x)
{
    return x == p[x] ? p[x] : p[x] = fr(p[x]) ;
}

void merge(int x , int y , int w)
{
    x = fr(x) , y = fr(y) ;
    if(x == y) return ;
    Cc[w] += cnt[x] * cnt[y] ;
    All = All + cnt[x] * cnt[y];
    p[x] = y ;
    cnt[y] += cnt[x];
}

signed main()
{
    //freopen("in","r",stdin);
    scanf("%lld",&t);
    while (t--){
        S.clear() ;
        st.clear() ;
        All = 0 ;
        for(int i = 0 ; i < N ; i++){
            p[i] = i ;
            cnt[i] = 1 ;
//            Cc[i] = 0 ;
        }
        memset(Cc,0,sizeof Cc);
        scanf("%lld%lld%lld",&n,&m,&Q) ;
        for(int i = 1 ; i <= m ; i++) {
            scanf("%lld%lld%lld",&Eg[i].x,&Eg[i].y,&Eg[i].w);
            st.insert(Eg[i].w) ;
        }
        for(int i = 1 ; i <= Q ; i++) scanf("%lld",&q[i]) , st.insert(q[i]) ;
        for(auto it : st) S.push_back(it);
        for(int i = 1 ; i <= m ; i++) {
            Eg[i].w = get(Eg[i].w);
        }
        for(int i = 1 ; i <= Q ; i++) q[i] = get(q[i]) ;
        sort(Eg+1,Eg+1+m);
        for(int i = 1 ; i <= m ; i++) {
            merge(Eg[i].x , Eg[i].y , Eg[i].w) ;
        }
        for(int i = 1 ; i < 2*N ; i++) {
            Cc[i] = Cc[i] + Cc[i-1] ;
        }
        for(int i = 1 ; i <= Q ; i++){
            //cout << q[i] <<  "***" << endl ;
            printf("%lld\n",All - Cc[q[i]-1]);
        }
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值