CF 1408 E. Avoid Rainbow Cycles

这篇博客介绍了如何将一个集合内的点互相连接的问题转化为二分图模型,并通过构建最大生成树来找到解决方案。作者首先简化问题,将集合内的点与一个虚拟点相连,形成一个二分图。接着证明了在这样的二分图中,如果存在环,则一定是彩虹环。最后,通过Prim算法或Kruskal算法求解最大生成树,从而得出答案。代码部分展示了如何实现这一过程。
摘要由CSDN通过智能技术生成

题目链接
思路
一个集合内的点都能互相到达,因此对于每个集合建立一个虚拟点,将其余点都与这个点相连那么就可表达集合内的点都能互相到达。简化一下模型就变成了一个二分图,左边有m个点,右边有n个点的二分图。
因此只要把左边的每个点和其对应的集合内部那几个点全部相连即可。可以证明这个二分图只要有环就一定是彩虹环。
证明:对于一个环 v 1 − e 1 − v 2 − e 2 − v 3 − e 3 − . . . − e k − v 1 v_1-e_1-v_2-e_2-v_3-e_3-...-e_k-v_1 v1e1v2e2v3e3...ekv1,若中间有两个点属于同一个集合,那么可以把这两个点缩起来。因为这两个点的连边属于集合 i i i,而这两个点之间的边都不是集合 i i i的边,依然形成了彩虹环。
那么就变成了对这个二分图求一个最大生成树即可。
代码

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
const int inf = 0x3f3f3f3f;


struct Edge {
	int u, v, w;
	friend bool operator < (const Edge &a, const Edge &b) {
		return a.w > b.w;
	}
}e[N];
int idx;
int fa[N];
int a[N], b[N];

int find(int x) {
	return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}

void solve() {
	int m, n;
	scanf("%d%d", &m, &n);
	for(int i = 1; i <= n + m; i++) {
		fa[i] = i;
	}
	for(int i = 1; i <= m; i++) {
		scanf("%d", &a[i]);
	}
	for(int i = 1; i <= n; i++) {
		scanf("%d", &b[i]);
	}
	for(int i = 1; i <= m; i++) {
		int k;
		scanf("%d", &k);
		while(k--) {
			int x;
			scanf("%d", &x);
			e[++idx] = {x, i + n, a[i] + b[x]};
		}
	}
	sort(e + 1, e + 1 + idx);
	LL res = 0;
	for(int i = 1; i <= idx; i++) {
		int fx = find(e[i].u), fy = find(e[i].v);
		if(fx != fy) {
			fa[fx] = fy;
		}
		else res += e[i].w;
	}
	printf("%lld\n", res);
}

int main() {
    // freopen("in.txt", "r", stdin);
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值