Solution
这道题我一开始打挂了,因为我没有考虑反转会对修改也产生影响。我们可以考虑建立一颗线段树来维护,对于每一个点开一个标记,标记的每一个二进制为表示在当前子树的第几层会全部翻转。我们只需要在修改和查询的时候把这个标记(push\_down)一下就可以了
另外注意在修改操作的时候,因为行与行之间会产生冲突,所以只能一行一行的修改。但中间的行都是完整的,所以度杂度是(mathcal{O}(1))。只有两端的位置也就是两行需要在线段上去修改,复杂度为(mathcal{O}(log n))
另外注意,在修改和查询的时候。在每一个点都要看一下在当前层有没有需要反转的标记,有就更改一下方向
Code
#include <bits/stdc++.h>
using namespace std;
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
const int maxn = 1e6 + 10;
int N, n, q, Pow[21];
namespace ST {
int A[maxn << 4], tag[maxn << 4];
#define ls (rt << 1)
#define rs (rt << 1 | 1)
void push_down(int rt) {
if (!tag[rt]) return;
tag[ls] ^= tag[rt], tag[rs] ^= tag[rt];
A[ls] ^= tag[rt], A[rs] ^= tag[rt];
tag[rt] = 0;
}
void change(int rt, int l, int r, int L, int R, int dep, int p) {
if (L <= l && r <= R) { A[rt] ^= Pow[p], tag[rt] ^= Pow[p]; return; }
push_down(rt);
int mid = (l + r) >> 1, tt = (A[rt] & Pow[dep]) ? 1 : 0;
if (L <= mid) change(ls ^ tt, l, mid, L, R, dep + 1, p);
if (R > mid) change(rs ^ tt, mid + 1, r, L, R, dep + 1, p);
}
int query(int rt, int l, int r, int x, int dep) {
if (l == r) return rt - (Pow[n] - 1);
push_down(rt);
int mid = (l + r) >> 1, tt = (A[rt] & Pow[dep]) ? 1 : 0;
if (x <= mid) return query(ls ^ tt, l, mid, x, dep + 1);
else return query(rs ^ tt, mid + 1, r, x, dep + 1);
}
}
int main() {
#ifdef xunzhen
freopen("33.in", "r", stdin);
freopen("33.out", "w", stdout);
#endif
n = read(); q = read(); N = 1 << n;
Pow[0] = 1; for (int i = 1; i <= 20; i++) Pow[i] = Pow[i - 1] << 1;
while (q--) {
int op = read();
if (op == 1) {
int l = read(), r = read();
for (int i = 0; i < n; i++)
if (l < Pow[i + 1] && r >= Pow[i]) {
int x = max(l, Pow[i]), y = min(r, Pow[i + 1] - 1);
if (x == Pow[i] && y == (Pow[i + 1] - 1)) ST::A[1] ^= Pow[i], ST::tag[1] ^= Pow[i];
else ST::change(1, 1, Pow[i], x - (Pow[i] - 1), y - (Pow[i] - 1), 0, i);
}
}else {
int x = read();
printf("%d
", ST::query(1, 1, N, x, 0));
}
}
return 0;
}
Summary
这道题要做出来,需要非常灵活地使用线段树
做之前要仔细考虑翻转会产生的影响