可以将每段区间的和转换成最高位的和+其他位的和.
- 查询 sum = lsum + rsum
- 减: 每个节点最多有三十次减操作,类似sqrt那个题目,直接在l==r的时候单点修改,注意需要判断当前能否减的条件是lsum > 0
- 加: 显然是节点最高位的和*2即可,维护lz表示倍数
const int maxn = 1e5 + 7;
const int mod = 998244353;
int n, t, m;
int a[maxn];
struct node {
ll l, r, lz, lsum, rsum;
} tr[maxn << 2];
void add(int p, int lz) {
tr[p].lz = lz * tr[p].lz % mod;
tr[p].lsum = tr[p].lsum * lz % mod;
}
void push_down(int p) {
if (tr[p].lz != 1) {
add(p << 1, tr[p].lz);
add(p << 1 | 1, tr[p].lz);
tr[p].lz = 1;
}
}
void push_up(int p) {
tr[p].lsum = (tr[p << 1].lsum + tr[p << 1 | 1].lsum) % mod;
tr[p].rsum = (tr[p << 1].rsum + tr[p << 1 | 1].rsum) % mod;
}
void build(int p, int l, int r) {
tr[p].l = l, tr[p].r = r, tr[p].lz = 1;
tr[p].lsum = tr[p].rsum = 0;
if (l == r) {
tr[p].lsum = a[l];
for (int i = 30; i >= 0; i--) {
if (a[l] & (1 << i)) {
tr[p].lsum = (1 << i);
tr[p].rsum = a[l] - tr[p].lsum;
break;
}
}
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
push_up(p);
}
void update(int p, int l, int r, int opt) {
if (tr[p].lsum == 0) return;
if (opt == 2) {
if (tr[p].l == tr[p].r) {
if (tr[p].rsum == 0) tr[p].lsum = 0;
else tr[p].rsum -= (tr[p].rsum & (-tr[p].rsum));
return;
}
} else {
if (l <= tr[p].l && tr[p].r <= r) {
add(p, 2);
return;
}
}
push_down(p);
int mi = (tr[p].l + tr[p].r) >> 1;
if (l <= mi) update(p << 1, l, r, opt);
if (r > mi) update(p << 1 | 1, l, r, opt);
push_up(p);
}
ll res = 0;
void query(int p, int l, int r) {
if (l <= tr[p].l && tr[p].r <= r) {
res += (tr[p].lsum + tr[p].rsum);
res %= mod;
return;
}
push_down(p);
int mi = (tr[p].l + tr[p].r) >> 1;
if (l <= mi) query(p << 1, l, r);
if (r > mi) query(p << 1 | 1, l, r);
push_up(p);
}
void solve() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1, 1, n);
scanf("%d", &m);
for (int i = 1, opt, l, r; i <= m; i++) {
scanf("%d%d%d", &opt, &l, &r);
if (opt == 1) {
res = 0, query(1, l, r);
printf("%lld\n", res);
} else {
update(1, l, r, opt);
}
}
}
}