P2574 XOR的艺术 区间修改,反转

解题思路

这道题目要求我们维护一个二进制字符串,支持两种操作:

  1. 区间取反:将指定区间内的0变成1,1变成0

  2. 区间查询:查询指定区间内1的个数

这是一个典型的区间修改和区间查询问题,可以使用线段树来高效解决。

方法选择

题目提供的代码使用了线段树解法,主要特点包括:

  1. 线段树节点:存储区间和(1的个数)和懒标记(表示是否需要取反)

  2. 区间取反:通过懒标记实现高效的区间修改

  3. 区间查询:直接查询区间和即可得到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;
}

 

posted @ 2025-05-21 12:01  CRt0729  阅读(14)  评论(0)    收藏  举报