题目大意
给定一个长度为N的序列,有M次操作,操作有几种:AND opn l r表示让l到r区间内所有的数与opn相与,OR opn l r表示让区间l到r内所有数与opn相或,XOR opn l r表示让区间l到r内所有数与opn异或,SUM l r表示求l到r区间的所有数的和
解题思路
我们的难点非常明显——怎么对这些数进行操作,因为在我们的感知内,位运算并没有加法结合律。
但是仔细看一下题,发现每一个元素只给了不超过15那么大的,不超过15,如果换成二进制位的话,只有四位,如果位运算一位一位的搞,最后再整合,同时,opn某一位上是0或者1分开来考虑,那么与和或就是覆盖0或者是覆盖1,异或操作就是直接取反,那不就恰好是HDU3397那种单个位上面的操作了吗?我们不必去考虑二进制的进位问题,我们只需要记录下来四位里面每一位有多少个1,那么最终整合的时候就可以得到正确的解
需要注意的地方
对于与和或操作,它们的优先级大于异或操作,也就是说当一个区间得到了这两个操作的时候,异或操作的标记应该清零。
代码
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 const int maxn = (int)1e6 + 10; int a[maxn], n, m; int cnt0[maxn << 2], cnt1[maxn << 2], flag[maxn << 2], flag_x[maxn << 2]; void fun(int iflag, int iflag_x, int l, int r, int rt){ if (iflag != -1){ flag[rt] = iflag; if (iflag == 0) cnt0[rt] = (r - l + 1), cnt1[rt] = 0; else if (iflag == 1) cnt1[rt] = (r - l + 1), cnt0[rt] = 0; flag_x[rt] = 0; } if (iflag_x){ flag_x[rt] ^= 1; swap(cnt0[rt], cnt1[rt]); } } void PushDown(int l, int r, int rt){ if (flag[rt] != -1 || flag_x[rt] == 1){ int m = (l + r) >> 1; fun(flag[rt], flag_x[rt], lson); fun(flag[rt], flag_x[rt], rson); flag[rt] = -1; flag_x[rt] = 0; } } void PushUp(int rt){ cnt0[rt] = cnt0[rt << 1] + cnt0[rt << 1 | 1]; cnt1[rt] = cnt1[rt << 1] + cnt1[rt << 1 | 1]; } void build(int l, int r, int rt, int bit){ cnt0[rt] = cnt1[rt] = flag_x[rt] = 0; flag[rt] = -1; if (l == r){ if ((a[l] & (1 << bit)) == 0) cnt0[rt] = 1; else cnt1[rt] = 1; return; } int m = (l + r) >> 1; build(lson, bit); build(rson, bit); PushUp(rt); } void update(int ll, int rr, int iflag, int iflag_x, int l, int r, int rt){ if (ll <= l && rr >= r){ fun(iflag, iflag_x, l, r, rt); return; } PushDown(l, r, rt); int m = (l + r) >> 1; if (ll <= m) update(ll, rr, iflag, iflag_x, lson); if (rr > m) update(ll, rr, iflag, iflag_x, rson); PushUp(rt); } int query(int ll, int rr, int l, int r, int rt){ if (ll <= l && rr >= r) return cnt1[rt]; PushDown(l, r, rt); int m = (l + r) >> 1; int num = 0; if (ll <= m) num += query(ll, rr, lson); if (rr > m) num += query(ll, rr, rson); PushUp(rt); return num; } struct OP{ char cmd[10]; int opn, l, r, cnt[5]; void get(){ memset(cnt, 0, sizeof(cnt)); scanf("%s", cmd); if (cmd[0] == 'S') scanf("%d%d", &l, &r); else scanf("%d%d%d", &opn, &l, &r); l = max(0, l); r = min(n - 1, r); } void print(){ for (int i = 0; i < 4; i++) printf("%d ", cnt[i]); puts(""); } }; OP op[100005]; void work(int bit){ build(0, n - 1, 1, bit); for (int i = 0; i < m; i++){ if (op[i].cmd[0] == 'A'){ if (op[i].opn & (1 << bit)) continue; else update(op[i].l, op[i].r, 0, 0, 0, n - 1, 1); }else if (op[i].cmd[0] == 'O'){ if (op[i].opn & (1 << bit)) update(op[i].l, op[i].r, 1, 0, 0, n - 1, 1); else continue; }else if (op[i].cmd[0] == 'X'){ if (op[i].opn & (1 << bit)) update(op[i].l, op[i].r, -1, 1, 0, n - 1, 1); else continue; } else op[i].cnt[bit] = query(op[i].l, op[i].r, 0, n - 1, 1); } } void solve(){ scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < m; i++) op[i].get(); for (int i = 0; i < 4; i++) work(i); for (int i = 0; i < m; i++) if (op[i].cmd[0] == 'S'){ int ans = op[i].cnt[0] + op[i].cnt[1] * 2 + op[i].cnt[2] * 4 + op[i].cnt[3] * 8; printf("%d\n", ans); } } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt", "rt", stdin); #endif int cs; cin >> cs; while(cs--) solve(); return 0; }