P2574 XOR的艺术 区间修改,反转
解题思路
这道题目要求我们维护一个二进制字符串,支持两种操作:
-
区间取反:将指定区间内的0变成1,1变成0
-
区间查询:查询指定区间内1的个数
这是一个典型的区间修改和区间查询问题,可以使用线段树来高效解决。
方法选择
题目提供的代码使用了线段树解法,主要特点包括:
-
线段树节点:存储区间和(1的个数)和懒标记(表示是否需要取反)
-
区间取反:通过懒标记实现高效的区间修改
-
区间查询:直接查询区间和即可得到1的个数
代码详细注释
#include<bits/stdc++.h> #define lc rt << 1 // 左子节点索引 #define rc rt << 1 | 1 // 右子节点索引 #define lson lc,l,mid // 左子树参数:左子节点,区间[l,mid] #define rson rc,mid + 1,r // 右子树参数:右子节点,区间[mid+1,r] #define ll long long using namespace std; const int N = 5e5 + 10, inf = 0x3f3f3f3f; // 线段树节点结构体 struct node{ ll sum; // 存储区间内1的个数 ll lazy; // 懒标记:0-无操作,1-需要取反 }; node t[N << 2]; // 线段树数组,大小是原数组的4倍 int n, m; // n-字符串长度,m-操作次数 int a[N]; // 原始数组,存储字符串转换后的数字 string s; // 原始字符串 // 更新父节点的信息 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; // 区间和等于左右子节点和相加 } // 下放懒标记操作 void down(int rt, int l, int r, ll num) { t[rt].sum = (r - l + 1) - t[rt].sum; // 取反操作:1的个数变为区间长度减去原个数 t[rt].lazy = !t[rt].lazy; // 切换懒标记状态(因为取反两次等于不操作) } // 下推懒标记到子节点 void pushdown(int rt, int l, int r) { if(t[rt].lazy == 0) return; // 无懒标记则返回 int mid = (l + r) / 2; down(lson, t[rt].lazy); // 下放到左子树 down(rson, t[rt].lazy); // 下放到右子树 t[rt].lazy = 0; // 清空当前节点的懒标记 } // 构建线段树 void build(int rt, int l, int r) { t[rt].lazy = 0; // 初始化懒标记 if(l == r) { // 叶子节点 t[rt].sum = a[l]; // 存储单个字符的值(0或1) return; } int mid = (l + r) / 2; build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新父节点信息 } // 区间修改函数(区间取反) void change(int rt, int l, int r, int x, int y) { if(r < x || y < l) return; // 完全不在区间内 if(x <= l && r <= y) { // 完全包含在区间内 t[rt].sum = (r - l + 1) - t[rt].sum; // 取反操作 t[rt].lazy = !t[rt].lazy; // 设置懒标记 return; } pushdown(rt, l, r); // 下放懒标记 int mid = (l + r) / 2; change(lson, x, y); // 修改左子树 change(rson, x, y); // 修改右子树 pushup(rt); // 更新父节点信息 } // 区间查询函数(查询1的个数) ll query(int rt, int l, int r, int x, int y) { if(r < x || y < l) return 0; // 区间无交集返回0 if(x <= l && r <= y) return t[rt].sum; // 完全包含直接返回区间和 pushdown(rt, l, r); // 下放懒标记 int mid = (l + r) / 2; // 返回左右子树查询结果的和 return query(lson, x, y) + query(rson, x, y); } int main() { cin >> n >> m >> s; // 读取字符串长度、操作次数和字符串 // 将字符串转换为数字数组 for(int i = 0; i < n; i++) a[i + 1] = s[i] - '0'; build(1, 1, n); // 构建线段树 while(m--) { // 处理每个操作 int op, x, y; scanf("%d%d%d", &op, &x, &y); // 读取操作类型和区间 if(op == 0) { change(1, 1, n, x, y); // 区间取反操作 } else { printf("%d\n", query(1, 1, n, x, y)); // 查询1的个数 } } return 0; }

浙公网安备 33010602011771号