并查集、树状数组

并查集、树状数组、线段树

  • 并查集
  • 树状数组
    • 树状数组1 (单点修改,区间查询)
    • 树状数组2 (单点查询,区间修改)

并查集

【模板】并查集

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N , M N,M N,M ,表示共有 N N N 个元素和 M M M 个操作。

接下来 M M M 行,每行包含三个整数 Z i , X i , Y i Z_i,X_i,Y_i Zi,Xi,Yi

Z i = 1 Z_i=1 Zi=1 时,将 X i X_i Xi Y i Y_i Yi 所在的集合合并。

Z i = 2 Z_i=2 Zi=2 时,输出 X i X_i Xi Y i Y_i Yi 是否在同一集合内,是的输出
Y ;否则输出 N

输出格式

对于每一个 Z i = 2 Z_i=2 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N

样例输入 #1

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

样例输出 #1

N
Y
N
Y

提示

对于 30 % 30\% 30% 的数据, N ≤ 10 N \le 10 N10 M ≤ 20 M \le 20 M20

对于 70 % 70\% 70% 的数据, N ≤ 100 N \le 100 N100 M ≤ 1 0 3 M \le 10^3 M103

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N \le 10^4 1N104 1 ≤ M ≤ 2 × 1 0 5 1\le M \le 2\times 10^5 1M2×105 1 ≤ X i , Y i ≤ N 1 \le X_i, Y_i \le N 1Xi,YiN Z i ∈ { 1 , 2 } Z_i \in \{ 1, 2 \} Zi{1,2}

以下是模板代码文章来源地址https://uudwc.com/A/vNX6g

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

int fa[MAXN]; // 用于存储每个元素所属的集合的根节点

// 查找操作,返回元素x所属集合的根节点
int find(int x) {
	if(x == fa[x]) return x; // 如果当前节点是根节点,直接返回
	return fa[x] = find(fa[x]); // 路径压缩,将x的父节点直接设为根节点,加快以后的查找
}

// 合并操作,将两个集合合并
void join(int c1, int c2) {
	int f1 = find(c1); // 查找c1所属的集合的根节点
	int f2 = find(c2); // 查找c2所属的集合的根节点
	if(f1 != f2) // 如果根节点不同,表示c1和c2不在同一集合中
		fa[f1] = f2; // 将c1的根节点的父节点设为c2的根节点,即合并两个集合
}

int main() {
	int n, m;
	cin >> n >> m; // 输入元素个数n和操作个数m
	for(int i = 1; i <= n; i++) fa[i] = i; // 初始化,每个元素初始时都是一个单独的集合,根节点就是自己
	
	while(m--) {
		int z, x, y;
		cin >> z >> x >> y; // 输入操作类型z以及两个元素x和y
		
		if(z == 1) {
			join(x, y); // 合并操作,将x和y所在的集合合并
		} else {
			if(find(x) == find(y))
				cout << "Y" << endl; // 查找操作,如果x和y在同一个集合中,输出Y
			else
				cout << "N" << endl; // 否则输出N
		}
	}
	
	return 0;
}

树状数组

树状数组1 (单点修改,区间查询)

【模板】树状数组 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 x x x

  • 求出某区间每一个数的和

输入格式

第一行包含两个正整数 n , m n,m n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

接下来 m m m 行每行包含 3 3 3 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 x x x 个数加上 k k k

  • 2 x y 含义:输出区间 [ x , y ] [x,y] [x,y] 内每个数的和

输出格式

输出包含若干行整数,即为所有操作 2 2 2 的结果。

样例输入 #1

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

样例输出 #1

14
16

提示

【数据范围】

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 8 1 \le n \le 8 1n8 1 ≤ m ≤ 10 1\le m \le 10 1m10
对于 70 % 70\% 70% 的数据, 1 ≤ n , m ≤ 1 0 4 1\le n,m \le 10^4 1n,m104
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 5 × 1 0 5 1\le n,m \le 5\times 10^5 1n,m5×105

数据保证对于任意时刻, a a a 的任意子区间(包括长度为 1 1 1 n n n 的子区间)和均在 [ − 2 31 , 2 31 ) [-2^{31}, 2^{31}) [231,231) 范围内。

样例说明:

故输出结果14、16

以下是模板代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
#define lowbit(x) ((x) & (-x))
int tree[N] = {0}; // 树状数组

void update(int x, int d) { // 单点修改:修改元素 a[x],a[x] = a[x] + d
	while (x <= N) {
		tree[x] += d; // 将当前位置的值增加d
		x += lowbit(x); // 转到下一个需要修改的位置
	}
}

int sum(int x) { // 查询前缀和:返回前缀和 sum = a[1] + a[2] + ... + a[x]
	int ans = 0;
	while (x > 0) {
		ans += tree[x]; // 累加当前位置的值
		x -= lowbit(x); // 转到前一个位置
	}
	return ans;
}

int main() {
	int n, m, a;
	cin >> n >> m; // 输入数列数字个数n和操作总个数m
	for (int i = 1; i <= n; i++) {
		cin >> a; // 输入每个数列项的初始值
		update(i, a); // 初始化计算tree[]数组
	}
	while (m--) {
		int op;
		cin >> op; // 输入操作类型
		if (op == 1) {
			int x, k;
			cin >> x >> k; // 输入要修改的元素位置x和要加的值k
			update(x, k); // 对位置x的元素进行加法操作
		} else {
			int x, y;
			cin >> x >> y; // 输入查询区间[x, y]
			cout << sum(y) - sum(x - 1) << endl; // 输出区间内元素和
		}
	}
	return 0;
}

树状数组2 (单点查询,区间修改)

【模板】树状数组 2

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 x x x

  2. 求出某一个数的值。

输入格式

第一行包含两个整数 N N N M M M,分别表示该数列数字的个数和操作的总个数。

第二行包含 N N N 个用空格分隔的整数,其中第 i i i 个数字表示数列第 $i $ 项的初始值。

接下来 M M M 行每行包含 2 2 2 4 4 4个整数,表示一个操作,具体如下:

操作 1 1 1: 格式:1 x y k 含义:将区间 [ x , y ] [x,y] [x,y] 内每个数加上 k k k

操作 2 2 2: 格式:2 x 含义:输出第 x x x 个数的值。

输出格式

输出包含若干行整数,即为所有操作 2 2 2 的结果。

样例输入 #1

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

样例输出 #1

6
10

提示

样例 1 解释:

故输出结果为 6、10。


数据规模与约定

对于 30 % 30\% 30% 的数据: N ≤ 8 N\le8 N8 M ≤ 10 M\le10 M10

对于 70 % 70\% 70% 的数据: N ≤ 10000 N\le 10000 N10000 M ≤ 10000 M\le10000 M10000

对于 100 % 100\% 100% 的数据: 1 ≤ N , M ≤ 500000 1 \leq N, M\le 500000 1N,M500000 1 ≤ x , y ≤ n 1 \leq x, y \leq n 1x,yn,保证任意时刻序列中任意元素的绝对值都不大于 2 30 2^{30} 230

以下是模板代码

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
#define lowbit(x) ((x) & (-x))
int tree[N] = {0}; // 树状数组

void update(int x, int d) { // 单点修改:修改元素 a[x],a[x] = a[x] + d
	while (x <= N) {
		tree[x] += d; // 将当前位置的值增加d
		x += lowbit(x); // 转到下一个需要修改的位置
	}
}

int sum(int x) { // 查询前缀和:返回前缀和 sum = a[1] + a[2] + ... + a[x]
	int ans = 0;
	while (x > 0) {
		ans += tree[x]; // 累加当前位置的值
		x -= lowbit(x); // 转到前一个位置
	}
	return ans;
}

int main() {
	int n, m;
	int old = 0, a;
	cin >> n >> m; // 输入数列数字个数n和操作总个数m
	for (int i = 1; i <= n; i++) {
		cin >> a; // 输入每个数列项的初始值
		update(i, a - old); // 初始化计算tree[]数组,这里是一个差分数组
		old = a;
	}
	while (m--) {
		int op;
		cin >> op; // 输入操作类型
		if (op == 1) {
			int x, y, k;
			cin >> x >> y >> k; // 输入要修改的区间[x, y]和要加的值k
			update(x, k);
			update(y + 1, -k); // 将区间[y+1, n]的值减去k,保持区间[x, y]加上k
		} else {
			int x;
			cin >> x; // 输入要查询的位置x
			cout << sum(x) << endl; // 输出第x个数的值
		}
	}
	return 0;
}

原文地址:https://blog.csdn.net/lcc1737/article/details/132248565

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

上一篇 2023年08月13日 08:44
下一篇 2023年08月13日 08:44