首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

并查集扩展

编程知识
2024年08月17日 11:15

并查集扩展

目录

普通并查集

例题:

1.洛谷P1197 星球大战

链接:

[P1197 JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

普通并查集+反向加点

解析:

正面删边很难,考虑反向求解,先求删完所有要求点的所有连通块数量,然后再一个一个加入,将问题简化

代码:

const int N = 400005;
struct edges{
	int v,ne;
}e[N << 1];
int h[N],idx = 0;
void add(int u,int v){
	e[idx] = {v,h[u]};
	h[u] = idx++;
}
int f[N];
int fd(int x){
	if(x==f[x]) return x;
	return f[x] = fd(f[x]);
}
int vis[N],ans[N];
int n,m;
void solve(){
	memset(h,-1,sizeof h);
	cin >> n >> m;
	for(int i = 1;i<=n;i++) f[i] = i;
	vector<int> b;
	for(int i = 1;i<=m;i++){
		int u,v;
		cin >> u >> v;
		u++,v++;
		add(v,u);
		add(u,v); 
	}
	int k;
	cin >> k;
	int res = n - k;
	for(int i = 0;i<k;i++){
		int t;
		cin >> t;
		t++;
		vis[t] = 1;
		b.push_back(t);
	}
	for(int i = 1;i<=n;i++){
		if(vis[i]) continue;
		for(int j = h[i];~j;j=e[j].ne){
			int v = e[j].v;
			if(vis[v]) continue;
			if(f[fd(i)] != f[fd(v)]){
				res--;
				f[fd(i)] = f[fd(v)];
			}
		}
	}
	ans[k+1] = res;
	for(int i = k - 1;i >= 0;i--){
		vis[b[i]] = 0;
		res++;
		for(int j = h[b[i]];~j;j=e[j].ne){
			int v = e[j].v;
			if(vis[v]) continue;
			if(f[fd(b[i])] != f[fd(v)]){
				res--;
				f[fd(b[i])] = f[fd(v)];
			}
		}
		ans[i + 1] = res;
	}
	for(int i = 1;i<=k+1;i++){
		cout << ans[i] << endl;
	}
}

2.洛谷P1955 程序自动分析

链接:

[P1955 NOI2015] 程序自动分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

普通并查集+离散化

解析:

普通并查集判断是否矛盾,数据大进行离散化

代码:

const int N = 200005;
struct DSU {
    vector<int> f;
    DSU(){}
    DSU(int n) {
        init(n);
    }
    void init(int n) {
        f.resize(n);
        iota(f.begin(), f.end(), 0);
    }
    int fd(int x) {
        if(f[x]==x) return x;
        return f[x] = fd(f[x]);
    }
    bool same(int x, int y) {
        return fd(x) == fd(y);
    }
    bool mg(int x, int y) {
        x = fd(x);
        y = fd(y);
        if (x == y)return false;
        f[y] = x;
        return true;
    }
};
int gt_idx(vector<int>& a,int i){
	int t = lower_bound(a.begin(),a.end(),i) - a.begin() + 1;
	return t;
}
void solve(){
	int n;
	cin >> n;
	vector<int> v1;
	vector<pair<int,int>> ys;
	vector<pair<int,int>> ns;
	vector<int> a;
	for(int i = 0;i < n;i++){
		int u,v,op;
		cin >> u >> v >> op;
		if(op==1){
			ys.push_back({u,v});
		}else{
			ns.push_back({u,v});
		}
		a.push_back(u);
		a.push_back(v);
	}
	sort(a.begin(),a.end());
	a.erase(unique(a.begin(),a.end()),a.end());
	int sz = a.size();
	DSU d(sz + 1);
	for(auto &[u,v]:ys){
		d.mg(gt_idx(a,u),gt_idx(a,v));
	}	
	for(auto &[u,v]:ns){
		if(d.fd(gt_idx(a,u)) == d.fd(gt_idx(a,v))){
			cout << "NO" << endl;
			return;
		}
	}
	cout << "YES" << endl;
}

带权并查集

例题:

1.洛谷P2024 食物链

链接:

[P2024 NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

带权并查集

解析:

维护%3关系,考虑合并时候维护集合关系,在同类时候d[u] - d[v]在%3意义下一定是0,x吃y那么一定是1,题目无需考虑2的情况,因为没有设置y吃x的正确性

代码:

const int N = 200005;
int f[N],d[N];
int n,m;
int fd(int x){
	if(x!=f[x]){
		int t = f[x];
		f[x] = fd(f[x]);
		d[x] += d[t];
	} 
	return f[x];
}
int calc(int x,int y){
	return ((x-y)%3 + 3)%3;
}
void merge(int x,int y,int v){
	int px = fd(x),py = fd(y);
	f[px] = py;
	d[px] = d[y] - d[x] + v;
}
void solve(){
	cin >> n >> m;
	iota(f,f+n+1,0);
	int cnt = 0;
	while(m--){
		int u,v,op;
		cin >> op >> u >> v;
		if(u>n||v>n){
			cnt++;
			continue;
		}
		if(op==1){
			if(fd(u)==fd(v) && calc(d[u],d[v]) != 0){
				cnt++;
				continue;
			}
			//d[v] = d[u] + d[fd(u)]
			//d[fd[u]] = d[v] - d[u]
			if(fd(u)!=fd(v)) merge(u,v,0);
		}else{
			if(fd(u)==fd(v) && calc(d[u],d[v]) != 1){
				cnt++;
				continue;
			}
			//d[v] = d[u] - 1  + d[fd(u)]
			//d[fd[u]] = d[v] - d[u] + 1
			if(fd(u)!=fd(v)) merge(u,v,1);
		}
	}
	cout << cnt << endl;
}

2.洛谷P1196 银河英雄传说

链接:

[P1196 NOI2002] 银河英雄传说 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

带权并查集

解析:

维护两个变量,一个是集合大小,一个是元素到根的距离,然后操作即可

代码:

const int N = 200005;
int sz[N],d[N],f[N];
void init(){
	for(int i = 0; i < N - 1;i++){
		f[i] = i;
		sz[i] = 1;
	}
}
int fd(int x){
	if(f[x]!=x){
		int t = f[x];
		f[x] = fd(f[x]);
		d[x] += d[t];
	}
	return f[x];
}
void merge(int x,int y){
	int px = fd(x),py = fd(y);
	f[px] = py;
	d[px] = sz[py];
	sz[py] += sz[px];
}
void solve(){
	int q;
	init();
	cin >> q;
	while(q--){
		int x,y;
		char op;
		cin >> op >>x >> y;
		if(op=='C'){
			if(fd(x)!=fd(y)){
				cout <<-1<<endl;
			}else{
				cout << abs(d[x]-d[y]) - 1 << endl;
			}
		}else{
			merge(x,y);
		}
	}
}

3.洛谷P5937 Parity Game

链接:

[P5937 CEOI1999] Parity Game - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

带权并查集

解析:

前缀和思想,合并 y 与 x - 1 判断奇偶性即可

代码:

const int N = 200005;
int n,q;
int idx = 0;
map<int,int> h;
int gt(int x){
	if(h.count(x)) return h[x];
	h[x] = ++idx;
	return idx;
}
int d[N],f[N];
int fd(int x){
	if(f[x] != x){
		int t = f[x];
		f[x] = fd(f[x]);
		d[x] += d[t];
	}
	return f[x];
}
void merge(int x,int y,int v){
	int px = fd(x),py = fd(y);
	f[px] = py;
	d[px] = d[y] - d[x] + v;
}
int calc(int x,int y){
	return ((d[x] - d[y])%2 + 2)%2;
}
void solve(){
	cin >> n >> q;
	int res = q;
	for(int i = 0;i<N;i++) f[i] = i;
	int id = 0;
	while(q--){
		int x,y;
		++id;
		string op;
		cin >> x >> y >> op;
		if(x==y&&op=="even"){
			res = id-1;
			break;
		}
		x = gt(x - 1);
		y = gt(y);
		if(op=="even"){
			if(fd(x)==fd(y)){
				if(calc(x,y) == 1){
					res = id - 1;
					break;
				}
			}else{
				merge(x,y,0);
			}
		}else{
			if(fd(x)==fd(y)){
				if(calc(x,y) == 0){
					res = id - 1;
					break;
				}
			}else{
				merge(x,y,1);
			}
		}
	}
	cout << res << endl;
}

扩展域并查集

例题:

1.洛谷P1525 关押罪犯

链接:

[P1525 NOIP2010 提高组] 关押罪犯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

类型:

扩展域并查集 + 带权并查集

解析:

方法一(扩展域并查集):

  • 两个集合,从大到小排序,考虑将敌对两个人分成两个集合,并且扩展域合并,敌人的敌人是朋友,就是设x有两个敌人 y z, 那么 z 与 x+n 建边, x +n 与 y 建边,通过 x+n这个虚点去连接应该在一个集合中的y与z,如果在同一集合,并且是敌对,那么直接输出即可

方法二(带权并查集):

  • 从大到小排序,避免前面的数在一个监狱,将两个集合合并起来,并且赋权值为1代表两个数不在同一监狱,于是在后面我们可以判断,如果两个数之前已经合并过并且mod操作位0代表,这两个集合一定要在一个监狱中,那么直接输出w,(否则,w会更大)

代码:

方法一:

int n,m;
struct qs{
	int u,v,w;
	bool operator<(const qs& q1)const{
		return w > q1.w;
	}
}; 
struct DSU {
    vector<int> f, sz;
    DSU(){}
    DSU(int n) {
        init(n);
    }
    void init(int n) {
        f.resize(n);
        iota(f.begin(), f.end(), 0);
        sz.assign(n, 1);
    }
    int fd(int x) {
        if(f[x]==x) return x;
        return f[x] = fd(f[x]);
    }
    bool mg(int x, int y) {
        x = fd(x);
        y = fd(y);
        if (x == y)return false;
        f[y] = x;
        return true;
    }
};

void solve(){
	cin >> n >> m;
	DSU d(2*(n+1));
	vector<qs> q;
	for(int i = 0; i < m;i++){
		int u,v,w;
		cin >> u >> v >> w;
		q.push_back({u,v,w});
	}
	sort(q.begin(),q.end());
	for(int i = 0;i < q.size();i++){
		int u = q[i].u,v = q[i].v,w = q[i].w;
		if(d.fd(u) == d.fd(v)){
			cout << w <<endl;
			return;
		}
		d.mg(u,v+n);
		d.mg(u+n,v);
	}
	cout << 0 << endl;
}

方法二:

struct qs
{
	int u,v,w;
	bool operator<(const qs &t)const{return w > t.w;};
};
int f[N],d[N];
int n,m;
int fd(int x){
	if(x!=f[x]){
		int t = f[x];
		f[x] = fd(f[x]);
		d[x] += d[t];
	} 
	return f[x];
}
int calc(int x,int y){
	return ((x-y)%2 + 2)%2;
}
void merge(int x,int y){
	int px = fd(x),py = fd(y);
	f[px] = py;
	d[px] = d[y] - d[x] + 1;
}
void solve(){
	cin >> n >> m;
	int q = m;
	vector<qs> a;
	iota(f,f+n+1,0);
	while(q--){
		int u,v,w;
		cin >> u >> v >> w;
		a.push_back({u,v,w});
	}
	sort(a.begin(),a.end());
	for(int i = 0; i < m;i++){
		int u = a[i].u,v = a[i].v,w = a[i].w;
		if(fd(u)==fd(v)){
			if(calc(d[u],d[v]) == 0){
				cout << w << endl;
				return;
			}
		}else{
			merge(u,v);
		}
	}
	cout << 0 <<endl;
}
From:https://www.cnblogs.com/bananawolf/p/18364209
本文地址: http://www.shuzixingkong.net/article/1182
0评论
提交 加载更多评论
其他文章 短视频上传怎么做|写个支持分片上传/断点续传/秒传功能的文件服务吧
各位平时使用的短视频应用,微信 & 微博等图文社区,它们的图文动态 & 视频上传的能力,都是极其核心的业务。 本质来说,这都是文件的上传,这篇文章带大家写一个文件上传服务,探究其核心原理,相信能为你带来一些帮助。
短视频上传怎么做|写个支持分片上传/断点续传/秒传功能的文件服务吧
使用 prefetchComponents 进行组件预取
title: 使用 prefetchComponents 进行组件预取 date: 2024/8/17 updated: 2024/8/17 author: cmdragon excerpt: 摘要:本文介绍Nuxt.js中的prefetchComponents功能,用于预取组件以提高用户体验。通过
使用 prefetchComponents 进行组件预取 使用 prefetchComponents 进行组件预取
1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到
今天刷面试题看到一个有意思的面试题, 1000T的文件怎么能以最快速度从南京传到北京? 网络传输 首先我们考虑通过网络传输,需要多长时间。 我特地咨询了在运营商工作的同学,目前带宽: 家庭宽带下行最大1Gbps,上行300M 企业级专线分数据专线和互联网专线: 数据专线 最大100Gbps,价格最低
1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到 1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到 1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
​IT寒冬之下,程序员这个职业不再像以往那么吃香,尤其是APP开发的门槛越来越高,使得安卓程序员不得不求变,如果不在技术上及时转型提高,逆水行舟未来不可期呀。 有鉴于此,博主整理了几个可供安卓程序员的技术转型发展方向,供大家参考。 1、继续深耕Android的应用开发 谷歌爸爸是安卓的爹,要想继续吃
T113s工业套件简述
T113s工业套件简述 提示 T113开发交流QQ群:120575746 此开发板的任何问题都可以在我们的论坛交流讨论&#160;https://forums.100ask.net/c/aw/ 硬件简述​ 100ASK_T113s3-Industrial-DevKit 是百问网设计的一款专门针对于工
T113s工业套件简述 T113s工业套件简述
debian10环境安装rtpengine
操作系统 :debian 10.13_x64 rtpengine版本:10.5 最新的debian12环境可通过apt直接安装rtpengine,但工作中有时候还会涉及到debian10这样的老系统,今天记录下debian10环境安装rtpengine的笔记,并提供相关演示效果及资源下载。 我将从以
debian10环境安装rtpengine debian10环境安装rtpengine debian10环境安装rtpengine
Jetpack架构组件学习(5)——Hilt 注入框架使用
原文: Jetpack架构组件学习(5)——Hilt 注入框架使用-Stars-One的杂货小窝 本篇需要有Kotlin基础知识,否则可能阅读本篇会有所困难! 介绍说明 实际上,郭霖那篇文章已经讲得比较明白了(具体参考链接都贴在下文了),这里简单总结下: 如果按照之前我们的MVC写法,我们可以直接在
Jetpack架构组件学习(5)——Hilt 注入框架使用 Jetpack架构组件学习(5)——Hilt 注入框架使用 Jetpack架构组件学习(5)——Hilt 注入框架使用
什么是AOP,以及在Springboot中自定义AOP
AOP (Aspect Oriented Programming)一般译为面向切面编程 Aspect [ˈ&#230;spekt] n.方面;层面;(动词的)体那么AOP 面相切面编程具体是指什么,它和之前的OOP 面相对象编程又有什么区别和联系。先说OOP,面相对象编程简单来说,万物皆可视为对象,
什么是AOP,以及在Springboot中自定义AOP 什么是AOP,以及在Springboot中自定义AOP