• Atcoder Nikki Qual E Bridge


    题意

    给一张无向图,点带权,边也带权

    要求在图中删去最少的边,使得对于每一条边,它所在连通块的点权之和大于其边权


    解法

    首先,看到题目里出现了删边,我们首先想到反向加边(如星球大战

    那么最后的答案就是(m-ans)(ans)为加入的边

    考虑一个边权为(w)的边(E(u,v))

    它是合法的,当且仅当(u)所在连通块与(v)所在连通块的点权之和大于(w)

    我们思考一下,如果它是合法的,将会给答案带来什么影响

    我们能够发现,边权(leq w)的边,如果能与(E)在同一连通块中,那么它们都会因为(E)的加入而变得合法(无论它们之前合法与否)

    那么这就给了我们启示:我们应该从小到大加边

    对于一个在当前不合法的边,我们不能直接舍弃,因为它有可能因为后来加入的某些边而变得合法

    因此我们开一个数组(cnt),用来记录某个连通块内目前不合法的边的数量

    当我们遇到一条当前合法的边,我们把它加入答案,并且把其连接的两个连通块内的(cnt)数组也一并计入答案,因为此时它们因为这条边而变得合法了

    维护连通块的操作用并查集实现,维护连通性与点权和,代码也很简单


    代码

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1e5 + 10;
    
    struct edge {
    	int u, v, w;
    	bool operator < (const edge &x) const {
    		return w < x.w;	
    	}
    } e[N]; 
    
    int n, m, ans;
    int sum[N], cnt[N];
    
    int id[N];
    
    inline int get(int x) {
    	return x == id[x] ? x : id[x] = get(id[x]);	
    }
    
    int main() {
    	
    	scanf("%d%d", &n, &m);
    	
    	for (int i = 1; i <= n; ++i)	
    		scanf("%d", sum + i);
    	for (int i = 1; i <= m; ++i)
    		scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    	
    	sort(e + 1, e + m + 1);
    	
    	for (int i = 1; i <= n; ++i)	id[i] = i;
    	
    	for (int i = 1; i <= m; ++i) {
    		int u = e[i].u, v = e[i].v;
    		int fu = get(u), fv = get(v);
    		if (fu != fv) {
    			sum[fu] += sum[fv];
    			cnt[fu] += cnt[fv];
    			cnt[fv] = sum[fv] = 0;
    			id[fv] = fu;	
    		}
    		cnt[fu]++;
    		if (sum[fu] >= e[i].w) {
    			ans += cnt[fu];
    			cnt[fu] = 0;
    		}
    	}
    	
    	printf("%d
    ", m - ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    Java控制台常用命令
    redis如何查看所有的key
    An internal error has occurred. Java heap space
    redis演练
    各种编程实现的树
    MYSQL两个数据库字符集保持一致问题
    进程控制之fork函数
    进程控制之进程标识符
    进程环境之getrlimit和setrlimit函数
    进程环境之setjmp和longjmp函数
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11443700.html
Copyright © 2020-2023  润新知