【算法学习】bitset 相关优化

bitset 基础用法

operator []: 访问其特定的一位。
operator ==/!=: 比较两个 bitset 内容是否完全一样。
operator &/&=/|/| =/^/^=/~: 进行按位与/或/异或/取反操作。
bitset 只能与 bitset 进行位运算,若要和整型进行位运算,要先将整型转换为 bitset。
operator <>/<<=/>>=: 进行二进制左移/右移。
operator <>: 流运算符,这意味着你可以通过 cin/cout 进行输入输出。
成员函数
count(): 返回 true 的数量。
size(): 返回 bitset 的大小。
test(pos): 它和 vector 中的 at() 的作用是一样的,和 [] 运算符的区别就是越界检查。
any(): 若存在某一位是 true 则返回 true,否则返回 false。
none(): 若所有位都是 false 则返回 true,否则返回 false。
all():C++11,若所有位都是 true 则返回 true,否则返回 false。
set(): 将整个 bitset 设置成 true。
set(pos, val = true): 将某一位设置成 true/false。
reset(): 将整个 bitset 设置成 false。
reset(pos): 将某一位设置成 false。相当于 set(pos, false)。
flip(): 翻转每一位。(相当于异或一个全是 1 的 bitset)
flip(pos): 翻转某一位。
to_string(): 返回转换成的字符串表达。
to_ullong():C++11,返回转换成的 unsigned long long 表达。
_Find_first(): 返回 bitset 第一个 true 的下标,若没有 true 则返回 bitset 的大小。
_Find_next(pos): 返回 pos 后面(下标严格大于 pos 的位置)第一个 true 的下标,
若 pos 后面没有 true 则返回 bitset 的大小。

bitset 优化例题

CF633G

首先可以知道:可以将子树通过 dfn 序转化为序列。

有一个思路是:维护大约 \(200\) 个树状数组来存储每个质数在区间中出现次数。复杂度 \(O(200\times n\log n)\)。在某些网站上可以通过(什么oj我不说)。

换一个思路,有一个限制:\(m\leq 1000\),这意味着我们完全可以将区间内开一个大小为 \(1000\) 的数组代表这个数是否出现。可以用线段树维护这类信息, push_up 时只需要将左子树维护的数组和右子树维护的数组或起来。区间加上一个数 \(x\) 相当于平移 \(x\) 单位,并且超出 \(m\) 范围的要循环移位。

复杂度 \(O(nm\log n)\)。肯定过不去。甚至还不如之前做法。

但是我们发现:我们数组维护的全是 01 信息,可以用 bitset 来代替。push_up 直接 tree[p].s = (tree[ls].s | tree[rs].s),区间加直接 tree[p].s = (((tree[p].s) >> (m - x)) | (tree[p].s << x))

复杂度 \(O(\dfrac{nm\log n}{32})\)。只放部分代码:

struct edge {
	int l, r, lazy;
	bitset<1005> s;
}tree[N * 4];
void push_up(int p) {
	tree[p].s = (tree[ls].s | tree[rs].s);
}
void down(int p, int x) {
	tree[p].lazy += x; tree[p].lazy %= m;
	tree[p].s = (((tree[p].s) >> (m - x)) | (tree[p].s << x));
}
void push_down(int p) {
	if (tree[p].lazy) {
		tree[p].lazy %= m;
		down(ls, tree[p].lazy); down(rs, tree[p].lazy);
		tree[p].lazy = 0;
	}
}

P5670

这题和上一道例题如出一辙,因为只看后 \(m\) 位,那前面的位其实就没有用了!等价于模上 \(2^m\)。这道题维护异或信息就行了。只放部分代码:

struct edge {
	int l, r, lazy;
	bitset<1024> s;
}tree[N * 4];

void push_up(int p) {
	tree[p].s = tree[ls].s ^ tree[rs].s;
}

void add(int p, int x) {
	tree[p].lazy = (tree[p].lazy + x) % m;
	tree[p].s = ((tree[p].s >> (1024 - x)) | (tree[p].s << x));
}

void push_down(int p) {
	if (tree[p].lazy) {
		add(ls, tree[p].lazy);
		add(rs, tree[p].lazy);
		tree[p].lazy = 0;
	}
}

signed main() {
	n = read(), m = read(), p = read(); m = (1 << m);
	for (int i = 1; i <= n; i++) a[i] = read();
	build(1, 1, n);
	while (p--) {
		int op, l, r, x;
		op = read(), l = read(), r = read();
		if (op == 1) {
			x = read();
			modify(1, l, r, x);
		} 
		else {
			ans.reset();
			query(1, l, r);
			int res = 0;
			for (int i = 0; i <= 1023; i++) if (ans[i]) res ^= i;
			wr(res & (m - 1));
			putchar('\n');
		}
	}
}

习题:

  • AT_abc258_g: [ABC258G] Triangle
  • P3674: 小清新人渣的本愿
  • P4688: 掉进兔子洞
  • P5355: 由乃的玉米田
posted @ 2024-03-06 21:34  Otue  阅读(44)  评论(0编辑  收藏  举报