CF1401F Reverse and Swap

CF1401F Reverse and Swap

非常抽象线段树,使我的大脑旋转。

考虑到建出的线段树一定是完全二叉树,且翻转或交换的区间一定满足都在一棵子树内,我们记 \(rev_{dep}\) 表示第 \(dep\) 层的左右子树是否交换(层数的定义是:从叶子往上,从 \(1\) 一直到 \(n + 1\) 层)。发现操作 \(3\) 等价于只交换第 \(k + 1\) 层,操作 \(2\) 等价于交换第 \(1 \sim k\) 层。(就是说翻转可以靠多次分治并从中点处左右交换实现)

现在考虑怎么快速实现交换。其实不用真的把整棵子树都交换,只需要交换左右儿子的编号就可以了。

时间复杂度 \(O(qn)\)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l); i <= (r); ++ i)
#define G(i,r,l) for(int i(r); i >= (l); -- i)
#define int ll
using namespace std;
using ll = long long;
char buf[100], *p1 = buf, *p2 = buf;
inline int gc(){
	return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, 100, stdin), p1 == p2) ? EOF : *p1 ++;
}
inline int rd(){
	int x = 0;
	char ch;
	while(!isdigit(ch = gc()));
	do x = (x << 3) + (x << 1) + (ch ^ 48); while(isdigit(ch = gc()));
	return x;
}
const int N = 300000;
int sum[N * 4];
bool rev[25]; // 打给自己, 且自己还没执行 
int n, m, q;
void pushup(int u){
	sum[u] = sum[u * 2] + sum[u * 2 + 1];
}
void update(int u, int l, int r, int x, int val, int dep){
	if(l == r){
		sum[u] = val;
		return ;
	}
	int mid = (l + r) / 2;
	if(rev[dep - 1]){
		if(x <= mid) update(u * 2 + 1, l, mid, x, val, dep - 1);
		else update(u * 2, mid + 1, r, x, val, dep - 1);
	}
	else{
		if(x <= mid) update(u * 2, l, mid, x, val, dep - 1);
		else update(u * 2 + 1, mid + 1, r, x, val, dep - 1);
	}
	pushup(u);
}
int query(int u, int l, int r, int x, int y, int dep){
	if(l >= x && r <= y){
		return sum[u];
	}
	int mid = (l + r) / 2, ret = 0;
	if(rev[dep - 1]){
		if(x <= mid) ret += query(u * 2 + 1, l, mid, x, y, dep - 1);
		if(y > mid) ret += query(u * 2, mid + 1, r, x, y, dep - 1);
	}
	else{
		if(x <= mid) ret += query(u * 2, l, mid, x, y, dep - 1);
		if(y > mid) ret += query(u * 2 + 1, mid + 1, r, x, y, dep - 1);
	}
	pushup(u);
	return ret;
}
signed main(){
	n = rd(), q = rd();
	m = 1 << n; 
	F(i, 1, m){
		int x = rd();
		update(1, 1, m, i, x, n + 1);
	} 
	while(q --){
		int opt = rd();
		if(opt == 1){
			int x = rd(), k = rd();
			update(1, 1, m, x, k, n + 1);
		}
		else if(opt == 2){
			int k = rd();
			if(k == 0) continue;
			F(i, 1, k) rev[i] ^= 1;
		}
		else if(opt == 3){
			int k = rd(); 
			rev[k + 1] ^= 1;
		}
		else{
			int l = rd(), r = rd();
			int ret = query(1, 1, m, l, r, n + 1);
			printf("%lld\n", ret);
		}
	}
	return 0;
}
posted @ 2025-08-08 17:59  superl61  阅读(3)  评论(0)    收藏  举报