A. Total Eclipse

2020杭电多校第二场A题

题意是有n个城市,之间有m条边,每个城市有一个bi属性,每选择一个点,与之联通的点的bi都会减一,问最少操作几次可以使全部bi为0。
思路是每次都选择最大的连通块,当出现一个bi=0后分为很多个更小的连通块,按照同样的方法操作。正着实现很麻烦,所以考虑倒过来的方法。
那么就可以将每次都把bi删为0看成一个个属性为bi的点插入进去。所以点插入的顺序应当是bi从大到小,因为正着来的话就是bi越小的点越早退出。
加入每个点 x 时遍历与 x 相连的所有边 (x,y),如果 y 在 x 之前加入且 x 和 y 不连通则将 x 和 y 合并,并将 y 所在连通块的树根的父亲设为 x,得到一棵有根树。那么每个点 x 在成为最小值之前已经被做了 b[father[x]] 次操作,所以每个点 x 对答案的贡献为 b[x] −b[father[x]]。 使用并查集支持路径压缩,时间复杂度 O((n + m)logn)。
具体实现如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e5+10;
const int M = 2e5+10;
const int INF = 0x3f3f3f3f;
const int inf = - INF;
const int mod = 1e9+7;
const double pi = acos(-1.0);
int n,m,cnt;
ll ans;
int edge[M<<1],nxt[M<<1],head[N];
int w[N],vis[N],f[N],fa[N],node[N];
void add(int x,int y){
    edge[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
    edge[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt;
}
bool cmp(int x,int y){
    return w[x]>w[y];
}
int find(int x){
    //printf("%d\n",fa[x]);
    return f[x]==x?x:f[x]=find(f[x]);//并查集
}
void init(){
    cnt=0;
    ans=0;
    for(int i=1;i<=n;i++){
        f[i]=i;
        fa[i]=0;
        vis[i]=0;
        head[i]=0;
    }
}
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=n;i++){
            scanf("%d",&w[i]);
            node[i]=i;
        }
        int x,y;
        while(m--){
            scanf("%d %d",&x,&y);
            add(x,y);
        }
        sort(node+1,node+1+n,cmp);
        for(int i=1;i<=n;i++){
            x=node[i];
            vis[x]=1;
            for(int j=head[x];j;j=nxt[j]){
                y=edge[j];
                //printf("%d %d\n",x,y);
                if(!vis[y]) continue;//还未加入的点
                y=find(y); 
                if(y==x) continue;//已经是x的子节点
                fa[y]=f[y]=x;//设x为y的父节点
            }
        }
        for(int i=1;i<=n;i++)
            ans+=w[i]-w[fa[i]];
        printf("%lld\n",ans);
    }    
    //system("pause");
    return 0;
}
/*
1
3 2
3 2 3
1 2
2 3
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值