牛客练习赛 29 E 位运算?位运算!(线段树)
题目链接 牛客练习赛29E
对$20$位分别建立线段树。首先$1$和$2$可以合起来搞(左移右移其实是等效的)
用个lazy标记下。转移的时候加个中间变量。
$3$和$4$其实就是区间$01$覆盖操作。
$5$就直接做就可以了。
#include <bits/stdc++.h> 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; }