题目大意:有一个整数 $x$ ,一开始为 $0$ 。有 $n$ 个操作,有两种类型:
$1 \;a\; b$:将 $x$ 加上整数 $a\cdot 2^b$ ,其中 $a$ 为一个整数, $b$ 为一个非负整数
$2\; k$ :询问 $x$ 在用二进制表示时,第 $2^k$ 位的值($0$或$1$)
保证在任何时候, $x\geq 0$ 。
题解:压位线段树,每一个节点压$30$位,考虑到有加减操作,而对于加操作,只是把这一位前的第一个$0$变成$1$,把其间的$1$变成$0$,减相反。所以我们可以在线段树中存一下区间$AND$和区间$OR$,找到某一位前的第一个$1$或第一个$0$,线段树修改一下就行了
卡点:线段树中的$update$和$pushdown$中把$rc$写成了$rt$($QAQ$)
C++ Code:
#include <cstdio>
#include <cstring>
#define base 30
#define maxn 1000010
using namespace std;
const int inf = (1 << base) - 1;
int n, op;
struct ST {
int And[maxn << 2], Or[maxn << 2], V[maxn << 2], tg[maxn << 2];
void init() {
memset(tg, -1, sizeof tg);
}
void pushdown(int rt) {
int lc = rt << 1, rc = rt << 1 | 1, &tmp = tg[rt];
And[lc] = Or[lc] = tg[lc] = tmp;
And[rc] = Or[rc] = tg[rc] = tmp;
tmp = -1;
}
void update(int rt) {
int lc = rt << 1, rc = rt << 1 | 1;
And[rt] = And[lc] & And[rc];
Or[rt] = Or[lc] | Or[rc];
}
void add(int rt, int l, int r, int p, int num) {
if (l > r) return ;
if (l == r) {
Or[rt] = And[rt] = num;
return ;
}
int mid = l + r >> 1;
if (~tg[rt]) pushdown(rt);
if (p <= mid) add(rt << 1, l, mid, p, num);
else add(rt << 1 | 1, mid + 1, r, p, num);
update(rt);
}
void add(int rt, int l, int r, int L, int R, int num) {
if (l > r || L > R) return ;
if (L <= l && R >= r) {
Or[rt] = And[rt] = tg[rt] = num;
return ;
}
int mid = l + r >> 1;
if (~tg[rt]) pushdown(rt);
if (L <= mid) add(rt << 1, l, mid, L, R, num);
if (R > mid) add(rt << 1 | 1, mid + 1, r, L, R, num);
update(rt);
}
int ask(int rt, int l, int r, int p) {
if (l > r) return 0;
if (l == r) return And[rt];
int mid = l + r >> 1, ans;
if (~tg[rt]) pushdown(rt);
if (p <= mid) ans = ask(rt << 1, l, mid, p);
else ans = ask(rt << 1 | 1, mid + 1, r, p);
return ans;
}
int find_1(int rt, int l, int r, int p) {
if (Or[rt] == 0) return -1;
if (l == r) return l;
int mid = l + r >> 1;
if (~tg[rt]) pushdown(rt);
if (p <= mid) {
int tmp = find_1(rt << 1, l, mid, p);
return ~tmp ? tmp : find_1(rt << 1 | 1, mid + 1, r, p);
} else return find_1(rt << 1 | 1, mid + 1, r, p);
}
int find_0(int rt, int l, int r, int p) {
if (And[rt] == inf) return -1;
if (l == r) return l;
int mid = l + r >> 1;
if (~tg[rt]) pushdown(rt);
if (p <= mid) {
int tmp = find_0(rt << 1, l, mid, p);
return ~tmp ? tmp : find_0(rt << 1 | 1, mid + 1, r, p);
} else return find_0(rt << 1 | 1, mid + 1, r, p);
}
} T;
void inc(int p, int num) {
if (!num) return ;
int tmp = T.ask(1, 0, maxn, p) + num;
if (tmp <= inf) T.add(1, 0, maxn, p, tmp);
else {
T.add(1, 0, maxn, p, tmp & inf);
int pos = T.find_0(1, 0, maxn, p + 1);
T.add(1, 0, maxn, p + 1, pos - 1, 0);
inc(pos, 1);
}
}
void dec(int p, int num) {
if (!num) return ;
int tmp = T.ask(1, 0, maxn, p) - num;
if (tmp >= 0) T.add(1, 0, maxn, p, tmp);
else {
T.add(1, 0, maxn, p, tmp + inf + 1);
int pos = T.find_1(1, 0, maxn, p + 1);
T.add(1, 0, maxn, p + 1, pos - 1, inf);
dec(pos, 1);
}
}
void modify(int a, int b) {
int blo = b / base, num = b % base;
if (a > 0) {
inc(blo, (a << num) & inf);
a = a >> base - num;
if (a) inc(blo + 1, a);
} else {
a = -a;
dec(blo, (a << num) & inf);
a = a >> base - num;
if (a) dec(blo + 1, a);
}
}
int query(int k) {
int tmp = T.ask(1, 0, maxn, k / base);
return (tmp & (1 << k % base)) && 1;
}
int main() {
// freopen("integer.in", "r", stdin);
// freopen("integer.out" ,"w", stdout);
scanf("%d%*d%*d%*d", &n);
T.init();
while (n--) {
scanf("%d", &op);
if (op --> 1) {
int k;
scanf("%d", &k);
// printf("%d\n", k);
printf("%d\n", query(k));
// puts("query");
} else {
int a, b;
scanf("%d%d", &a, &b);
if (!a) continue;
modify(a, b);
// puts("modify");
}
}
return 0;
}