首页 > 其他分享 >并查集处理区间跳跃

并查集处理区间跳跃

时间:2023-08-10 22:33:32浏览次数:41  
标签:nxt idx fa int 查集 pos 区间 跳跃 find

在网上胡乱找的一些关于并查集处理区间跳跃(也有叫区间覆盖/序列联通性,这类问题有没有什么统一叫法存疑?)的题目,或许能学习后成为一种套路

参考:

区间跳跃问题

Knight Tournament

板子题

维护一个并查集\(nxt\),\(nxt[i]\)为从\(i\)开始(包含\(i\))的第一个没有被打败的骑士的编号

并查集初始化的时候记得到多初始化一个到\(nxt[n+1]\)

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

const int N = 3e5 + 10;
int nxt[N], ans[N];

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

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n + 1; ++ i) nxt[i] = i;
	while (m -- ) {
		int l, r, x;
		cin >> l >> r >> x;
		for (int i = find(l); i <= r; i = find(i)) {
			if (i != x) {
				ans[i] = x;
				nxt[i] = i + 1;
			}
			else i = i + 1;
		}
	}
	for (int i = 1; i <= n; ++ i) cout << ans[i] << ' ';

	return 0;
}

疯狂的馒头/白雪皑皑

后面的操作会覆盖前面的操作,考虑可以倒着染色,思路跟上题一样,依旧是板子题

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

const int N = 1e6 + 10;
int nxt[N], ans[N];

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

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n, m, p, q;
	cin >> n >> m >> p >> q;
	for (int i = 1; i <= n + 1; ++ i) nxt[i] = i;

	for (int i = m; i; -- i) {
		int l = (i * p + q) % n + 1;
		int r = (i * q + p) % n + 1;
		if (l > r) swap(l, r);

		for (int j = find(l); j <= r; j = find(j)) {
			ans[j] = i;
			nxt[j] = j + 1;
		}
	}

	for (int i = 1; i <= n; ++ i) cout << ans[i] << '\n';

	return 0;
}

Two Teams

分别用并查集维护从\(i\)开始从左往右第一个没有被选择的人的编号和从右往左第一个没有被选择的人的编号

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

const int N = 2e5 + 10;
int a[N], ans[N], idx[N];

struct DSU {
	int fa[N];
	DSU() {
		iota(fa, fa + N, 0);
	}

	int find(int x) {
		if (x != fa[x]) fa[x] = find(fa[x]);
		return fa[x];
	}
	void merge(int a, int b) {
		int p = find(a), q = find(b);
		fa[p] = q;
	}
};

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++ i) {
		cin >> a[i];
		idx[a[i]] = i;
	}
	DSU pre, nxt;

	int maxv = n, tag = 1;
	while (1) {
		while (ans[idx[maxv]]) -- maxv;
		if (!maxv) break;
		int pos = idx[maxv];
		ans[pos] = tag;
		pre.merge(pos, pos - 1);
		nxt.merge(pos, pos + 1);
		int i = pre.find(pos), cnt = 0;

		while (i >= 1 && cnt < k) {
			ans[i] = tag;
			pre.merge(i, i - 1);
			nxt.merge(i, i + 1);
			++ cnt;
			i = pre.find(i);
		}
		i = nxt.find(pos), cnt = 0;
		while (i <= n && cnt < k) {
			ans[i] = tag;
			pre.merge(i, i - 1);
			nxt.merge(i, i + 1);
			++ cnt;
			i = nxt.find(i);
		}
		tag = 3 - tag;
	}

	for (int i = 1; i <= n; ++ i) cout << ans[i];

	return 0;
}

Professor Higashikata

思路参考:

Codeforces Round 882 (Div. 2)(A-D,待更新)

Codeforces Round 882 (Div. 2) A - D, F

\(t(s)\)中越靠前的位置为\(1\),字典序也相应越大,想到\(s[i]\)在 \(t(s)\)中的位置越前,将其变为1的贡献也越大

考虑维护\(s[i]\)在\(t(s)\)中第一次出现的位置,并按出现位置的先后对\(i\)排序,顺序越前的\(i\)越优先将\(s[i]\)变为\(1\),维护第一次出现的位置的过程使用并查集,后续用树状数组,详细可以见上面两篇博客

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

const int N = 2e5 + 10;
int n, m, q, idx[N];
int fa[N], fen[N];

int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void merge(int a, int b) {
	int p = find(a), q = find(b);
	fa[p] = q;
}

int lowbit(int x) { return x & -x; }
void modify(int x, int d) {
	while (x <= n) fen[x] += d, x += lowbit(x);
}
int query(int x) {
	int ans = 0;
	while (x) ans += fen[x], x -= lowbit(x);
	return ans;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> n >> m >> q;
	string s;
	cin >> s;
	s = ' ' + s;
	for (int i = 1; i <= n + 1; ++ i) fa[i] = i;

	int tot = 0;
	for (int i = 1; i <= n; ++ i)
		if (s[i] == '1') ++ tot;

	int cnt = 0;
	for (int i = 1; i <= m; ++ i) {
		int l, r;
		cin >> l >> r;
		for (int j = find(l); j <= r; j = find(j)) {
			idx[j] = ++ cnt;
			if (s[j] == '1') modify(idx[j], 1);
			merge(j, j + 1);
		}
	}

	while (q -- ) {
		int x;
		cin >> x;
		if (s[x] == '1') {
			s[x] = '0';
			-- tot;
			if (idx[x]) modify(idx[x], -1);
		}
		else {
			s[x] = '1';
			++ tot;
			if (idx[x]) modify(idx[x], 1);
		}
		if (cnt <= tot) cout << cnt - query(cnt) << '\n';
		else cout << tot - query(tot) << '\n';
	}

	return 0;
}

标签:nxt,idx,fa,int,查集,pos,区间,跳跃,find
From: https://www.cnblogs.com/Panmaru/p/17621773.html

相关文章

  • 「学习笔记」并查集
    真的有必要说吗?直接上封装好的模板吧,包含路径压缩和按秩合并。structunion_find_set{intfa[N],siz[N];int&operator[](constint&x){returnfa[x];}voidreset(){for(inti=1;i<=n;++i){fa[i]=i;......
  • 7999: 路径图 并查集
    描述 给定一个n个顶点(1~n编号),m条边的简单无向图,判断是否是一个路径图。路径图要求:必须存在一个顶点序列v1,v2,...,vn,它是1~n的一个排列,且对于任何1<=i<=n-1,vi和vi+1之间有边相连,而对于任何1<=i,j<=n(其中|i-j|>=2),vi和vj之间没有边相连。 输入 第一行为两个正......
  • 区间 dp
    模板区间dp一个长\(n(n\le248)\)的序列,选择数列中两个相邻且相等的元素,删去其中一个元素并使另一个元素的值\(+1\),求数次操作后数列中的最大值将这看做是合并的过程\(dp_{i,j}\)表示区间\([i,j]\)和为一个答案的取值,这里的取值其实是唯一的,所以可以之间判断对于每......
  • Codeforces 1857E:Power of Points 区间?
    1857E.PowerofPointsDescription:\(n\)个数:\(x_1,···,x_n\),从左向右扫,当\(s=x_i\)时,可以将这\(n\)个数分为若干个闭区间\([s,x_1],[s,x_2],···,[s,x_n]\)(当然如果\(x_i<s\),则区间形如\([x_i,s]\))对于每一个\(s\in(x_1,···,x_n),\)有一个整数\(p\),记\(f_......
  • 造船(并查集)思路详解
    造船题目描述:题目描述小Y想要在虚拟世界里造船。最开始m个船的完成度全部都为0。小Y第i时刻可以在a_i和b_i两艘船中选择一艘让这艘船的完成度。由于国家政府是奇数控,所以所有偶数完成度的船只都将被摧毁,小Y想知道m时刻后能剩下来的船只最多有多少艘。输入格式第一行两个......
  • 区间DP
    Smiling&Weeping----你站在桥上看风景,看风景的人在楼上看你。明月装饰了你的窗子,你装饰......
  • 王道408--数据结构--用数组实现二叉树--并查集及其优化代码
    一、数组实现二叉树(下标从0开始)#include<stdio.h>typedefstruct_TreeNode{intdata;boolIsEmpty;//结点是否为空//因为我们的二叉树不一定是满二叉树,中间可能有一些节点不存在//值为1代表空}TreeNode;//初始化voidInitTreeNode(TreeNodet[......
  • 2023-08-06:小青蛙住在一条河边, 它想到河对岸的学校去学习 小青蛙打算经过河里 的石头
    2023-08-06:小青蛙住在一条河边,它想到河对岸的学校去学习小青蛙打算经过河里的石头跳到对岸河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上给定一个长度为n的数组arr,表示每块儿石头的高度数值每块石头有一个高度,每次小青蛙从一块石头起跳这块石头的高度就......
  • 2023-08-06:小青蛙住在一条河边, 它想到河对岸的学校去学习 小青蛙打算经过河里 的石头
    2023-08-06:小青蛙住在一条河边,它想到河对岸的学校去学习小青蛙打算经过河里的石头跳到对岸河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上给定一个长度为n的数组arr,表示每块儿石头的高度数值每块石头有一个高度,每次小青蛙从一块石头起跳这块石头的......
  • 8/5并查集
    #include<bits/stdc++.h>usingnamespacestd;intn,m;intparent[1000005];voidinit(intn){for(inti=1;i<=n;i++){parent[i]=i;}}intfind(intx){if(parent[x]==x){returnx;}else{parent[x]=find......