补题链接: https://acm.hdu.edu.cn/showproblem.php?pid=7059
考点: 线段树
解题思路可参考
但是与下面的代码不同的是: 下面代码考虑的是flag = true
代表[l, r]区间内ai为0,即高位和低位都为0;
AC Code
using namespace std;
typedef long long ll;
const int mod = 998244353, N = 1e5 + 10;
int n, q;
// x 位代表a的二进制表示中最左边的1的位置
// lo: 记录a的前x-1位的大小
// hi: 记录a的x位的10进制表示的大小
ll lo[N], hi[N];
struct SegmentTree{
int l, r; // 区间
ll sum1, sum2; // lo的区间和; hi的区间和
bool flag; // [l, r]区间每个ai都是0
ll tag2; // ai + 2^k(相当于最高位*2)的懒标记; tag2的值代表[l, r]区间的每个hi需要乘多少
}tree[N << 2];
ll lowbit(ll x){
return x & (-x);
}
void build(int node, int l, int r){
// 初始化
tree[node].l = l, tree[node].r = r;
tree[node].flag = 0;
tree[node].tag2 = 1;
if(l == r){
tree[node].sum1 = lo[l];
tree[node].sum2 = hi[l];
// 如果[l, r]的ai除最高位外,其余位都为0(sum1=0),flag=true;
if(!tree[node].sum2) tree[node].flag = 1;
return;
}
int mid = (l + r) >> 1;
int left_node = node << 1;
int right_node = node << 1 | 1;
build(left_node, l, mid);
build(right_node, mid + 1, r);
tree[node].sum1 = (tree[left_node].sum1 + tree[right_node].sum1) % mod;
tree[node].sum2 = (tree[left_node].sum2 + tree[right_node].sum2) % mod;
// 只有两边都为0; 父节点才为0
tree[node].flag = tree[left_node].flag & tree[right_node].flag;
}
// 关于pushDown操作: 当某操作向当前节点的子节点走时,一定要先执行pushDown操作
void pushDown(int node){
int left_node = node << 1;
int right_node = node << 1 | 1;
tree[left_node].tag2 = tree[left_node].tag2 * tree[node].tag2 % mod;
tree[right_node].tag2 = tree[right_node].tag2 * tree[node].tag2 % mod;
tree[left_node].sum2 = tree[left_node].sum2 * tree[node].tag2 % mod;
tree[right_node].sum2 = tree[right_node].sum2 * tree[node].tag2 % mod;
tree[node].tag2 = 1;
}
// 暴力lowbit操作
void modify1(int node, int L, int R){
if(tree[node].l == tree[node].r){
// 如果当前ai的除了最高位都为0
if(!tree[node].sum1){
// 将 最高位 置0
tree[node].sum2 = 0;
tree[node].flag = 1;
tree[node].tag2 = 1;
return;
}
tree[node].sum1 -= lowbit(tree[node].sum1);
return;
}
// 如果当前区间内所有位都是0
// 所以tag2要变为1
if(L <= tree[node].l && tree[node].r <= R && tree[node].flag){
// tree[node].sum1 = tree[node].sum2 = 0;
tree[node].tag2 = 1;
return;
}
pushDown(node);
int mid = (tree[node].l + tree[node].r) >> 1;
int left_node = node << 1;
int right_node = node << 1 | 1;
if(L <= mid) modify1(left_node, L, R);
if(R > mid) modify1(right_node, L, R);
tree[node].sum1 = (tree[left_node].sum1 + tree[right_node].sum1) % mod;
tree[node].sum2 = (tree[left_node].sum2 + tree[right_node].sum2) % mod;
tree[node].flag = tree[left_node].flag & tree[right_node].flag;
}
// + 2^k操作, 相当于高位*2
void modify2(int node, int L, int R){
if(L <= tree[node].l && tree[node].r <= R){
tree[node].sum2 = tree[node].sum2 * 2 % mod;
tree[node].tag2 = tree[node].tag2 * 2 % mod;
return;
}
pushDown(node);
int mid = (tree[node].l + tree[node].r) >> 1;
int left_node = node << 1;
int right_node = node << 1 | 1;
if(L <= mid) modify2(left_node, L, R);
if(R > mid) modify2(right_node, L, R);
tree[node].sum1 = (tree[left_node].sum1 + tree[right_node].sum1) % mod;
tree[node].sum2 = (tree[left_node].sum2 + tree[right_node].sum2) % mod;
tree[node].flag = tree[left_node].flag & tree[right_node].flag;
}
// query
ll ask(int node, int L, int R){
if(L <= tree[node].l && tree[node].r <= R){
return (tree[node].sum1 + tree[node].sum2) % mod;
}
pushDown(node);
ll res = 0;
int mid = (tree[node].l + tree[node].r) >> 1;
int left_node = node << 1;
int right_node = node << 1 | 1;
if(L <= mid) res += ask(left_node, L, R);
if(R > mid) res = (res + ask(right_node, L, R)) % mod;
return res;
}
int main(){
int t;
scanf("%d", &t);
while (t--){
scanf("%d", &n);
ll a;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a);
hi[i] = lo[i] = 0; // 清空
for (int j = 30; j > 0; j--) {
// 计算a的lo值和hi值
if((1ll << j) <= a){
hi[i] = (1ll << j);
lo[i] = a & (~hi[i]);
break;
}
}
}
// 建树
build(1, 1, n);
scanf("%d", &q);
for (int i = 1; i <= q; ++i) {
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
if(opt == 1){
printf("%lld\n", ask(1, l, r));
}else if(opt == 2){
modify1(1, l, r);
}else{
modify2(1, l, r);
}
}
}
return 0;
}