Counting Stars

我们把一个数拆出最高位\(x\)和其他位\(y\)来单独考虑,例如\(5\)可以拆分成\(4\)\(1\),那么操作一就是最高位\(x\)乘2,操作二就是其他位删除\(1\)个二进制下\(1\)的个数,如果\(y\)已经是\(0\)了,因为\(x\)只含有最高位的\(1\)\(1\),我们还需要将\(x\)置为\(0\),当\(x\)也为\(0\)后,表示之后对于它的操作都是无效的了,我们用一个标记\(flag\)来表示它已经不需要操作了,当一个区间\(flag\)\(true\)时,我们可以直接返回,减少复杂度。

对于有效的操作一,操作二,我们用线段树分别维护即可。

对于操作一,我们只需要区间乘\(2\)的操作。

对于操作二,我们可以暴力遍历每一个\(flag\)不为\(true\)的区间,因为每个点最多有\(log_2(1e9)\)次操作二,所以加上\(flag\)标记后时间复杂度也在可接受范围内。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5 + 5;
const int MOD = 998244353;
int lowbit(int x) {
    return x & -x;
}
// 找到最大的满足条件的2^k<=x
int find(int x) {
    int res = 1;
    while (res <= x) {
        res <<= 1;
    }
    return res >> 1;
}
int a[MAXN], b[MAXN];
#define ls(x) (x) << 1
#define rs(x) (x) << 1 | 1
struct SegmentTreeNode {
    int l, r;
    int high, low; // high为最高位的值, low为其他位值
    int mul; // 标记
    bool flag; // 是否全部清空
    int mid() {
        return (l + r) >> 1;
    }
}tree[MAXN << 2];
void pushup(int p) {
    tree[p].high = (tree[ls(p)].high + tree[rs(p)].high) % MOD;
    tree[p].low = (tree[ls(p)].low + tree[rs(p)].low) % MOD;
    tree[p].flag = tree[ls(p)].flag & tree[rs(p)].flag;
}
void pushdown(int p) {
    if (tree[p].mul != 1) {
        tree[ls(p)].high = (tree[ls(p)].high * tree[p].mul) % MOD;
		tree[rs(p)].high = (tree[rs(p)].high * tree[p].mul) % MOD;
		tree[ls(p)].mul = (tree[ls(p)].mul * tree[p].mul) % MOD;
		tree[rs(p)].mul = (tree[rs(p)].mul * tree[p].mul) % MOD;
        tree[p].mul = 1;
    }
}
void build(int l, int r, int p) {
    tree[p].l = l, tree[p].r = r;
    tree[p].mul = 1;
    if (l == r) {
        tree[p].high = a[l];
        tree[p].low = b[l];
        tree[p].flag = false;
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls(p));
    build(mid + 1, r, rs(p));
    pushup(p);
}
// 单点修改
void update1(int l, int r, int p) {
    if (tree[p].flag) return ; // 全部为0直接返回
    if (tree[p].l == tree[p].r) {
        if (tree[p].low) {
            tree[p].low -= lowbit(tree[p].low);
        }
        // 如果其他位值为0那么将最高位置0并标记
        else {
            tree[p].high = 0;
            tree[p].flag = true;
        }
        return ;
    }
    pushdown(p);
    int mid = tree[p].mid();
    if (l <= mid) update1(l, r, ls(p));
    if (r > mid) update1(l, r, rs(p));
    pushup(p);
}
// 区间乘操作
void update2(int l, int r, int p) {
    if (tree[p].flag) return ; // 全部为0直接返回
    if (l <= tree[p].l && tree[p].r <= r) {
        tree[p].mul = (tree[p].mul * 2) % MOD;
        tree[p].high = (tree[p].high * 2) % MOD;
        return ;
    }
    pushdown(p);
    int mid = tree[p].mid();
    if (l <= mid) update2(l, r, ls(p));
    if (r > mid) update2(l, r, rs(p));
    pushup(p);
}
int query(int l, int r, int p) {
    if (l <= tree[p].l && tree[p].r <= r) {
        return (tree[p].high + tree[p].low) % MOD;
    }
    pushdown(p);
    int mid = tree[p].mid(), res = 0;
    if (l <= mid) res = (res + query(l, r, ls(p))) % MOD;
    if (r > mid) res = (res + query(l, r, rs(p))) % MOD;
    return res;
}
signed main(int argc, char *argv[]) {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
            int t = find(a[i]);
            b[i] = a[i] - t, a[i] = t;
            // a存储最高位, b存储其他位
        }
        build(1, n, 1);
        int m;
        cin >> m;
        while (m--) {
            int opt, l, r;
            cin >> opt >> l >> r;
            if (l > r) swap(l, r);
            if (opt == 1) {
                cout << query(l, r, 1) << '\n';
            }
            if (opt == 2) {
                update1(l, r, 1);
            }
            if (opt == 3) {
                update2(l, r, 1);
            }
        }
    }
	system("pause");
	return 0;
}