kuangbin 专题五: 并查集

1. Wireless Network

/*
这道算是并查集的模板题了,主要需要弄清楚的是连接的条件。在这连接的条件是连个点之间的距离要小于等于d(即题目给定的距离),与此同时还要判断你要连接的电脑是否已经被修理过了,因为只有被修好的电脑才能充当中转站。
最终本题的思路就是,先接受输入的点的坐标,然后接受若干个操作,当操作为O时,枚举所有点与当前点之间的关系(包括距离和是否被修复)符合条件就把两点连接。如果是S操作,那就判断两点是否连接,连接就输出SUCCESS否则输出FAIL。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;

struct Node{
	int x, y;
}nodes[N];

int n, d;
int a, b;
int p[N], f[N];
char op;

//判断两点减是否能进行连接
bool judge(Node u, Node v) {
	return (u.x - v.x) * (u.x - v.x) + (u.y - v.y) * (u.y - v.y) <= d * d;
}

//找根节点
int find(int x) {
	if(x == p[x]) return x;
	return p[x] = find(p[x]);
}

//合并
void merge(int x, int y) {
	int px = find(x);
	int py = find(y);
	if(px != py) {
		p[px] = py;
	}
}

int main() {
	scanf("%d%d", &n, &d);
	for(int i = 1; i <= n; i++) scanf("%d%d", &nodes[i].x, &nodes[i].y), p[i] = i;
	while(~scanf(" %c", &op)) {
		if(op == 'O') {
			scanf("%d", &a);
			f[a] = 1;
        //枚举所有点看看哪些点可以进行相连
			for(int i = 1; i <= n; i++) {
				if(judge(nodes[a], nodes[i]) && f[i]) merge(i, a);
			}
		} else {
			scanf("%d%d", &a, &b);
			if(find(a) == find(b)) puts("SUCCESS");
			else puts("FAIL");
		}
	}
}

2. The Suspects

/*
这是一道维护集合大小的并查集
把所有需要连接在一起的点连在一起
最后输出0号点的根的集合大小即可
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;

int n, m;
int a, b, len;
int p[N], s[N];

//找根结点
int find(int x) {
	if(x == p[x]) return x;
	return p[x] = find(p[x]);
}

//合并
void merge(int x, int y) {
	int px = find(x);
	int py = find(y);
	if(px != py) {
		p[px] = py;
		s[py] += s[px];
	}
}

int main() {
	while(~scanf("%d%d", &n, &m) && (n || m)) {
		for(int i = 0; i <= n; i++) p[i] = i, s[i] = 1;
		for(int i = 0; i < m; i++) {
			scanf("%d%d", &len, &a);
			for(int i = 1; i < len; i++) scanf("%d", &b), merge(a, b);
		}
     //找到0号结点的父结点并输出其集合大小
		int ans = find(0);
		printf("%d\n", s[ans]);
	}
}

3. How Many Tables

/*
假设一开始每人一桌,每当有两个人之间可以merge就说明他们可以做一桌。
此时需要的桌子数就可以减少一个。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;

int t, n, m;
int a, b, cnt;
int p[N];

//找根结点
int find(int x) {
	if(x == p[x]) return x;
	return p[x] = find(p[x]);
}

//合并
void merge(int x, int y) {
	int px = find(x);
	int py = find(y);
	if(px != py) p[px] = py, cnt++;
}

int main() {
	scanf("%d", &t);
	while(t--) {
		cnt = 0;
		scanf("%d%d", &n, &m);
		for(int i = 0; i <= n; i++) p[i] = i;
		for(int i = 0; i < m; i++) scanf("%d%d",&a, &b), merge(a, b);
		printf("%d\n", n - cnt);
	}
}

4. How Many Answers Are Wrong

/*
这道题是典型的带边权的并查集做的,对于并查集的基本知识我在这里就不多讲了,许多博客都有介绍,这里我重点说说这个带权边的构造问题,这也是带权边问题的核心。
首先带权边的主要用法是,我维护当前点到根结点的距离,然后当查看集合中两点的关系时,我们可以通过查看两个点到根结点的关系,以此来寻找两个点之间的相对关系。
先讲讲这道题的大致思路,这道题首先可以用前缀和的思想,用并查集的方法来做,具体就是把每个点的前缀和转化为边权。每次查询时,我们找到两点相对边权即为我们要的val。
这道题的带权边的维护关键在于式子**d[pe] = val - d[e] + d[s]** 这个式子的推导大致如下:首先我们维护边权的目的是使下次检测时**d[e] - d[s] == val** 。在这种情况下,给定的关系是对的,否则就是错的。维护出这种关系我们只要给子节点赋予边权d[pe] = val - d[e] + d[s]就好了。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2000010;

int t, n, m;
int a, b, z, ans;
int p[N], d[N];

//找根结点
int find(int x) {
	if(x != p[x]) {
		int tmp = find(p[x]);
		d[x] += d[p[x]];
		p[x] = tmp;
	}
	return p[x];
}

int main() {
	while(~scanf("%d%d", &n, &m)) {
		for(int i = 0; i <= n; i++) p[i] = i, d[i] = 0;
		ans = 0;
		while(m--) {
			scanf("%d%d%d", &a, &b, &z);
			a--;
			int pa = find(a), pb = find(b);
        //已经连接则判断是否矛盾
			if(pa == pb) {
				if(d[a] - d[b] != z) ans++;
        //连接两点
			} else {
				p[pa] = pb;
				d[pa] = z + d[b] - d[a];
			}
		}
		printf("%d\n", ans);
	}
}

5. 食物链

/*
道题我用的是带边权的并查集来做的(用拓展域也可以)。带边权的并查集主要思路是,首先创建一个d数组用于维护边权。在find函数中,在找根的同时找到当前点到根节点的边权。而每次我们在查询两点间的关系时都可以通过相对边权(即两个点到根结点的边权只差)来查找。
以这道题为例,这道题的核心问题是如何才能通过边权来找到两个点之间的关系呢?
**这道题的核心思路**就是把所有的结点分成三组(及题目中的A,B,C)然后把当前边权求模3,比如当前点的边权求模为0时,该点在组A;当前点的边权求模为1时,该点在组B;当前点的边权求模为2时,该点在组C。这样我们就可以判断当前点所在的组了。因为我们要利用边权求两点间的相对关系,所以我们没必要太专注于当前点到底是A,B,C中的那一组。我们要求的是两点间的相对关系。
那**具体的方法**就是把他们的相对边权mod3。如果同组则它们的相对边权求应该为0,即(d[x] - d[y]) % 3) == 0;如果是捕食关系,那么它们的相对边权mod3应该还差1,即(d[x] - d[y] - 1) % 3 == 0。根据这一性质,我们就可以判断是否两点同组(或捕食关系),也可以以后这种方式把两点连接起来(连接为同组关系或捕食关系)。
核心思路讲完现在讲讲**整体思路**,首先按照上面的思路写出find和merge(两种连接方法)函数,然后每次接受操作和x,y。如果x和y大于n则直接记为错误,否则往后如果是操作1,则看这两个点是否已连接,如果未连接则连接,已连接则判断这两点间的关系(具体方法上面讲了),如果矛盾ans++,不矛盾则继续。操作2的操作与操作1基本相似,除了判断条件和连接方法。最后输出答案即可
*/
//边带权
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 50010;
int n, m, ans = 0;
int p[N], d[N];

//初始化父结点
void init() {
	for (int i = 1; i <= n; i++) p[i] = i;
}

//找到根节点,同时维护边权
int find(int x) {
    if (p[x] == x) return x;
    int tmp = find(p[x]);
    d[x] += d[p[x]];
    return p[x] = tmp;
}

//连接两点之间的捕食关系
void mergeEat(int x, int y) {
	int px = find(x);
	int py = find(y);
	if (px != py) {
        p[px] = py;
        d[px] = d[y] +1 - d[x];
    }
}

//连接两个点之间的同类关系
void mergeSame(int x, int y) {
	int px = find(x);
	int py = find(y);
	if (px != py) {
        p[px] = py;
        d[px] = d[y] - d[x];
    }
}

int main() {
    scanf("%d%d", &n, &m);
    //初始化
	init();
    while (m--) {
        int t, x, y;
        scanf("%d%d%d", &t, &x, &y);
        //第二种假话x或y大于n
        if (x > n || y > n) ans++ ;
        else {
            int px = find(x), py = find(y);
            //第一种即x和y同类
            if (t == 1) {
                //先判断此话是否矛盾(具体判断见下方解释)
                if (px == py && (d[x] - d[y]) % 3) ans++ ;
                //以同类的方式连接结点
                else mergeSame(x, y);
            }
            //第二种即x吃y
            else {
                //先判断此话是否矛盾(具体判断见下方解释)
                if (px == py && (d[x] - d[y] - 1) % 3) ans++ ;
                //以捕食的方式连接结点
                else mergeEat(x, y);
            }
        }
    }
    //最后输出答案
    printf("%d\n", ans);
    return 0;
}

//拓展域
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;

int p[N], n, m, op, a, b, ans;
//初始化父结点
void init() {
    for(int i = 1; i < N; i++) p[i] = i;
}
//查找某个点的根结点
int find(int x) {
    if(p[x] == x) return p[x];
    return p[x] = find(p[x]);
}
//合并两个点所在的集合
void merge(int x, int y) {
    p[find(x)] = find(y);
}

int main() {
    scanf("%d%d", &n, &m);
    init();
    while(m--) {
        scanf("%d%d%d", &op, &a, &b);
        //有任何一个数字大于n的情况
        if(a > n || b > n) {
            ans++;
            continue;
        }
        //把点分为三种情况,分别为本身,作为捕食者,作为食物
        int as = a, ae = a + n, af = a + n + n;
        int bs = b, be = b + n, bf = b + n + n;
        //如果是同类的情况
        if(op == 1) {
        	//查看他们之间是否存在捕食的关系
            if(find(ae) == find(bs) || find(as) == find(be)) ans++;
            //查看他们间是否已经合并
            else if(find(as) != find(bs)) {
            //既然他们是同类那么他们的食物和天敌的区间应该是相同的
                merge(as, bs);
                merge(ae, be);
                merge(af, bf);
            }
        } else {
        	//他们间是否为同类或者他们的不是关系是否反过来了
            if(find(as) == find(bs) || find(as) == find(be)) ans++;
            //判断没问题后查看他们之间是否已经合并
            else if(find(ae) != find(bs)) {
            	//表示a吃b
                merge(ae, bs);
                //表示b被a吃
                merge(as, bf);
                //表示a被b的食物吃(这个关系很重要不要漏掉)
                merge(af, be);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

6. True Liars

//dp + 并查集,待补~

7. Supermarket

/*
这道题主要是利用了路径压缩的方法,使得我们在寻找可售时间时的速度更快
具体做法见注释
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10010;

int n, ans;
int p[N];

struct item {
	int w, d;
    //按照商品的价值进行排序,价值相等保质期久的排前面
	bool operator < (const item &t)const {
		if(w == t.w) return d > t.d;
		return w > t.w;
	}
} s[N];

int find(int x) {
	if(x != p[x]) p[x] = find(p[x]);
	return p[x];
}

int main() {
	while(~scanf("%d", &n)) {
		ans = 0;
		for(int i = 1; i <= n; i++) scanf("%d%d", &s[i].w, &s[i].d);
		for(int i = 0; i < N; i++) p[i] = i;
		sort(s + 1, s + n + 1);
		for(int i = 1; i <= n; i++) {
            //day为0说明这个物品已经过保质期无法再选
			int day = find(s[i].d);
			if(day) {
				ans += s[i].w;
                //父节点直接指向下一个有空的位置(为0就说明没空位了)
				p[day] = find(day - 1);
			}
		}
		printf("%d\n", ans);
	}
}

8. Parity game

/*
这道题是带权并查集 + 离散化的一道题
首先由于每次是询问一个区间1数量的奇偶性,所以我们需要离散化出现过的坐标
然后我们每次看看把查询区间的点进操作,如果两个点之间不处在同一个集合中,我们无法判断出是否有矛盾
因此我们姑且相信它是对的,我们用边权维护下这一信息
当两个点处在同一集合中时,我们就可以判断是否产生矛盾了
如果出现矛盾,则输出该序列符合了前X个问题和答案
如果序列符合所有问题的要求,应输出问题的数量q。 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2000010;

int n, q, ans;
int p[N], d[N];
int a[N], b[N];
vector<int> alls;
char op[N][4];

//查询离散化后的值
int ask(int x) {
	return lower_bound(alls.begin(), alls.end(), x) - alls.begin();
}

//找根节点
int find(int x) {
	if(p[x] != x) {
		int tmp = find(p[x]);
        //维护信息
		d[x] ^= d[p[x]];
		p[x] = tmp;
	}
	return p[x];
}

int main() {
	scanf("%d%d", &n, &q);
	for(int i = 0; i < q; i++) {
		scanf("%d%d%s", &a[i], &b[i], op[i]);
        //根据前缀和的求法我们这里要把左边界-1
		a[i]--;
		alls.push_back(a[i]), alls.push_back(b[i]);
	}
    //离散化
	sort(alls.begin(), alls.end());
	alls.erase(unique(alls.begin(), alls.end()), alls.end());
	n = alls.size();
	for(int i = 1; i <= n; i++) p[i] = i;
	for(int i = 0; i < q; i++) {
		int l = ask(a[i]), r = ask(b[i]);
		int pl = find(l), pr = find(r);
        //若已经合并
		if(pl == pr) {
			if(op[i][0] == 'e') {
				if(d[r] ^ d[l] != 0) {
					printf("%d\n", i);
					return 0;
				}
			} else {
				if(d[r] ^ d[l] != 1) {
					printf("%d\n", i);
					return 0;
				}
			}
         //如果还没有合并则合并两个区间
		} else {
			p[pl] = pr;
			if(op[i][0] == 'e') d[pl] = d[r] ^ d[l] ^ 0;
			else d[pl] = d[r] ^ d[l] ^ 1;
		}
	}
	printf("%d\n", q);
}

9. Navigation Nightmare

/*
这道题目的大致题意是有n个网格状的农田,每个农田之间有距离,会依次给出关系,在给出关系后询问两个农田之间的曼哈顿距离是多少
这道题还会有k次查询且规定只使用前i行的信息,所以我们需要每次处理完第i组数据以后看看有没有查询刚好是用到前i组数据
有我们就可以对其进行查询了
每次我们对要维护两个权值分别是水平方向上的权值,至于因该维护哪个方向取绝于方向是什么
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2000010;

int n, m, k, tmp;
int a[N], b[N], z[N];
int p[N], d1[N], d2[N];
char d[N];

//用于记录查询
struct ask {
	int u, v, c;
	bool operator < (const ask &w)const {
		return c < w.c;
	}
} q[N];

//找到父结点
int find(int x) {
	if(x != p[x]) {
		int tmp = find(p[x]);
		d1[x] += d1[p[x]];
		d2[x] += d2[p[x]];
		p[x] = tmp;
	}
	return p[x];
}

int main() {
	while(~scanf("%d%d", &n, &m)) {
	   for(int i = 0; i <= n; i++) p[i] = i;
	   memset(q, 0, sizeof q);
		for(int i = 1; i <= m; i++) scanf("%d%d%d %c", &a[i], &b[i], &z[i], &d[i]);
		scanf("%d", &k);
		for(int i = 0; i < k; i++) scanf("%d%d%d", &q[i].u, &q[i].v, &q[i].c);
     //把所有的查询按照
		sort(q, q + k);
		for(int i = 0, j = 1; i < k; i++) {
			for(;j <= q[i].c; j++){
				int pa = find(a[j]), pb = find(b[j]);
				p[pb] = pa;
           //根据相对位置的不同维护水平方向和垂直方向上的相对边权
				if(d[j] == 'N'){
					d1[pb] = z[j] + d1[a[j]] - d1[b[j]];
					d2[pb] = d2[a[j]] - d2[b[j]];
				} else if(d[j] == 'S') {
					d1[pb] = d1[a[j]] - d1[b[j]] - z[j];
					d2[pb] = d2[a[j]] - d2[b[j]];
				} else if(d[j] == 'W') {
					d2[pb] = d2[a[j]] - d2[b[j]] - z[j];
					d1[pb] = d1[a[j]] - d1[b[j]];
				} else {
					d2[pb] = z[j] + d2[a[j]] - d2[b[j]];
					d1[pb] = d1[a[j]] - d1[b[j]];
				}
			}
			if(find(q[i].u) == find(q[i].v)) printf("%d\n", abs(d1[q[i].u] - d1[q[i].v]) + abs(d2[q[i].u] - d2[q[i].v]));
			else puts("-1");
		}
		puts("");
	}
}

10. A Bug’s Life

/*

*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2000010;

int t, n, m;
int p[N], d[N];
int a, b, f, ca;

int find(int x) {
	if(p[x] != x) {
		int tmp = find(p[x]);
		d[x] ^= d[p[x]];
		p[x] = tmp;
	}
	return p[x];
}

int main() {
	scanf("%d", &t);
	while(t--) {
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++) p[i] = i, d[i] = 0;
		f = 0;
		while(m--) {
			scanf("%d%d", &a, &b);
			int pa = find(a), pb = find(b);
			if(pa == pb) {
				if(!(d[a] ^ d[b])) f = 1;
			} else {
				p[pa] = pb;
				d[pa] = d[a] ^ d[b] ^ 1;
			}
		}
		printf("Scenario #%d:\n", ++ca);
		if(f) puts("Suspicious bugs found!");
		else puts("No suspicious bugs found!");
		puts("");
	}
}

11. Rochambeau

/*
这道题在可以看成是食物链那道题的拓展,也是有三个互相克制的关系,然后维护他们的关系找出矛盾之类的东西
但是这道题还有一些特别的地方
这道题我们需要找到一个裁判这个裁判无论怎么出现矛盾都没问题,接下来我们就要寻找这个裁判是谁
那么我们可以枚举每一个人,然后看看除去他以外其他人形成的集合是否能做到没矛盾,可以则说明这个人可以作为裁判
当所有人枚举往以后,我们看看有多少人可以成为裁判如果为0则说明没有,如果为1则说明符合要求,并记录最后一个矛盾点
如果大于一则说明无法确定
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 2010;

struct Node{
    int u, v, op;
}a[N];
int n, m, c;
int p[N], d[N];

void init() {
    for(int i = 0; i <= n; i++) p[i] = i, d[i] = 0;
}

int find(int x) {
    if(p[x] != x) {
        int y = find(p[x]);
        d[x] += d[p[x]];
        p[x] = y;
    }
    return p[x];
}

int main() {
    while(~scanf("%d%d", &n, &m)) {
        //先把字符转为数字
        for(int i = 1; i <= m; i++) {
            scanf("%d %c%d", &a[i].u, &c, &a[i].v);
            if(c == '<') a[i].op = 2;
            else if(c == '>') a[i].op = 1;
            else a[i].op = 0;
        }
        int cnt = 0, cur = 0, q = 0;
        //枚举每一个人作为裁判
        for(int i = 0; i < n; i++) {
            init();
            int f = 1;
            //枚举每一个关系
            for(int j = 1 ; j <= m; j++) {
                //涉及到第i个人的关系跳过不处理
                if(a[j].u == i || a[j].v == i) continue;
                int u = a[j].u, v = a[j].v, c = a[j].op;
                int fu = find(u), fv = find(v);
                if(fu == fv) {
                    if(((d[u] - d[v]) % 3 + 3) % 3 != c) {
                        f = 0;
                        //维护最后一个矛盾出现的位置
                        q = max(q, j);
                        break;
                    }
                } else {
                    p[fu] = fv;
                    d[fu] = ((d[v] - d[u] + c) % 3 + 3) % 3;
                }
            }
            if(f) {
                cnt++;
                cur = i;
            }
        }
        if(cnt == 0) puts("Impossible");
        else if(cnt > 1) puts("Can not determine");
        else printf("Player %d can be determined to be the judge after %d lines\n", cur, q);
    }
}

12. Connections in Galaxy War

//并查集 + 离线处理,待补~

13. 小希的迷宫

/*
这道题问的是是否能把给定的结点通过给定的关系构成以可树,这道题我们看看哪些情况无法构成树:
1.维护并查集时出现了环
2.维护完以后出现了森林
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int N = 100010;

int a, b;
int p[N];
set<int> hs;

int find(int x) {
    if(p[x] == x) return x;
    return p[x] = find(p[x]);
}

int main() {
    while(~scanf("%d%d", &a, &b)) {
        if(a == b && a == -1) break;
        if(a == 0 && b == 0) {
            puts("Yes");
            continue;
        }
        hs.clear();
        hs.insert(a), hs.insert(b);
        //标记当前状态是否可以构成树
        int f = 1;
        for(int i = 1; i < N; i++) p[i] = i;
        p[a] = b;
        while(~scanf("%d%d", &a, &b) && (a || b)) {
            int pa = find(a), pb = find(b);
            hs.insert(a), hs.insert(b);
            //出现了环
            if(pa == pb) f = 0;
            else p[pa] = pb;
        }
        int h = -1;
        for(set<int>::iterator i = hs.begin(); i != hs.end() && f; i++) {
            int cur = *i;
            //拿第一点的祖宗节点作为标准
            if(h == -1) h = find(cur);
            else {
                //如果出现其他情况说明出现森林
                if(h != find(cur)) {
                    f = 0;
                    break;
                }
            }
        }
        if(f) puts("Yes");
        else puts("No");
    }
}

14. Is It A Tree?

/*
这道题遇上一题相似
但还是要注意一些细节(细节卡了我好久555)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> 
#include<set>
using namespace std;
const int N = 100010;

int a, b, ca;
int p[N];
set<int> hs;

int find(int x) {
    if(p[x] == x) return x;
    return p[x] = find(p[x]);
}

int main() {
    while(~scanf("%d%d", &a, &b) && (a != -1 || b != -1)) {
        if(a == 0 && b == 0) {
            printf("Case %d is a tree.\n", ++ca);
            continue;
        }
        int f = 1;
        for(int i = 1; i < N; i++) p[i] = i;
        hs.clear();
        hs.insert(a), hs.insert(b);
        p[a] = b;
        if(a == b) f = 0;
        while(~scanf("%d%d", &a, &b) && (a || b)) {
            hs.insert(a), hs.insert(b);
            int pa = find(a), pb = find(b);
            //出现自己连自己的情况也不算
            if(pa == pb || a == b) f = 0;
            else p[pa] = pb;
        }
        int cnt = 0;
        for(set<int>::iterator i = hs.begin(); i != hs.end(); i++) {
            int cur = *i;
            if(find(cur) == cur) cnt++;
        }
        if(cnt > 1) f = 0;
        if(f) printf("Case %d is a tree.\n", ++ca);
        else printf("Case %d is not a tree.\n", ++ca);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin带你飞」专题并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值