牛客练习赛 29 E 位运算?位运算!(线段树)

题目链接  牛客练习赛29E

对$20$位分别建立线段树。首先$1$和$2$可以合起来搞(左移右移其实是等效的)

用个lazy标记下。转移的时候加个中间变量。

$3$和$4$其实就是区间$01$覆盖操作。

$5$就直接做就可以了。

#include 

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 2e5 + 10;

int s[N << 2][21], tmp[21], lazy[N << 2];
int a[N];
int n, q;
int op, l, r, v;
LL  ans;

void rotate(int x, int v){
	rep(i, 0, 19) tmp[i] = s[x][(i + v) % 20];
	rep(i, 0, 19) s[x][i] = tmp[i];
	lazy[x] += v;
	lazy[x] %= 20;
}


void build(int x, int l, int r){
	if (l == r){
		rep(i, 0, 19) s[x][i] = (a[l] >> i) & 1;
		return;
	}

	int mid = (l + r) >> 1;
	build(x << 1, l, mid);
	build(x << 1 | 1, mid + 1, r);
	rep(i, 0, 19) s[x][i] = s[x << 1][i] + s[x << 1 | 1][i];
}

void update(int x, int L, int R){
	if (l <= L && R <= r){
		if (op == 1) rotate(x, v);
		else if (op == 2) rotate(x, 20 - v);
		else if (op == 3){
			rep(i, 0, 19) if ((v >> i) & 1) s[x][i] = R - L + 1;
		}

		else if (op == 4){
			rep(i, 0, 19) if (!((v >> i) & 1)) s[x][i] = 0;
		}

		else{
			rep(i, 0, 19) ans += ((LL)s[x][i] << i);
		}

		return ;
	}

	int len = R - L + 1, mid = (L + R) >> 1;

	if (lazy[x]){
		rotate(x << 1, lazy[x]);
		rotate(x << 1 | 1, lazy[x]);
		lazy[x] = 0;
	}

	rep(i, 0, 19){
		if (s[x][i] == len){
			s[x << 1][i] = len - len / 2;
			s[x << 1 | 1][i] = len / 2;
		}

		else if (s[x][i] == 0){
			s[x << 1][i] = 0;
			s[x << 1 | 1][i] = 0;
		}

	}

	if (l <= mid) update(x << 1, L, mid);
	if (r  > mid) update(x << 1 | 1, mid + 1, R);
	rep(i, 0, 19) s[x][i] = s[x << 1][i] + s[x << 1 | 1][i];
}
			
		


int main(){

	scanf("%d%d", &n, &q);

	rep(i, 1, n) scanf("%d", a + i);
	build(1, 1, n);

	while (q--){
		scanf("%d%d%d%d", &op, &l, &r, &v);	
		ans = 0;
		update(1, 1, n);
		if (op == 5) printf("%lld\n", ans);
	}

	return 0;
}

  

转载于:https://www.cnblogs.com/cxhscst2/p/9821671.html

你可能感兴趣的:(牛客练习赛 29 E 位运算?位运算!(线段树))