【洛谷】P2574 XOR的艺术

该文介绍了如何使用带懒惰标记的线段树来解决一个字符串处理问题,其中涉及到区间内0和1的转换以及查询特定区间内1的数量。在每次操作中,可以对指定区间进行0和1的翻转,或者询问该区间内1的个数。线段树的懒惰标记用于优化区间修改和查询的效率,使其达到O(logn)的时间复杂度。
摘要由CSDN通过智能技术生成

题目地址:

https://www.luogu.com.cn/problem/P2574

题目描述:
AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下:
1.拥有一个伤害串,是一个长度为 n n n的只含字符0和字符1的字符串。规定这个字符串的首字符是第一个字符,即下标从 1 1 1开始。
2.给定一个范围 [ l ,   r ] [l,~r] [l, r],伤害为伤害串的这个范围内中字符1的个数
3.会修改伤害串中的数值,修改的方法是把 [ l ,   r ] [l,~r] [l, r]中所有原来的字符0变成1,将1变成0
AKN 想知道一些时刻的伤害,请你帮助他求出这个伤害。

输入格式:
输入的第一行有两个用空格隔开的整数,分别表示伤害串的长度 n n n,和操作的个数 m m m
输入第二行是一个长度为 n n n的字符串 S S S,代表伤害串。
3 3 3到第 ( m + 2 ) (m + 2) (m+2)行,每行有三个用空格隔开的整数 o p , l , r op, l, r op,l,r。代表第 i i i次操作的方式和区间,规则是:
o p = 0 op = 0 op=0,则表示将伤害串的 [ l ,   r ] [l,~r] [l, r]区间内的0变成11变成0
o p = 1 op = 1 op=1,则表示询问伤害串的 [ l ,   r ] [l,~r] [l, r]区间内有多少个字符1

输出格式:
对于每次询问,输出一行一个整数,代表区间内1的个数。

数据范围:
对于 10 % 10\% 10%的数据,保证 n , m ≤ 10 n, m \leq 10 n,m10
另有 30 % 30\% 30%的数据,保证 n , m ≤ 2 × 1 0 3 n, m \leq 2 \times 10^3 n,m2×103
对于 100 % 100\% 100%的数据,保证 2 ≤ n , m ≤ 2 × 1 0 5 2 \leq n, m \leq 2 \times 10^5 2n,m2×105 0 ≤ o p ≤ 1 0 \leq op \leq 1 0op1 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1lrn S S S中只含字符0和字符1

可以用带懒标记的线段树,懒标记记录当前区间是否要整体异或 1 1 1。那么两个操作分别对应区间修改和区间求和。代码如下:

#include <iostream>
using namespace std;

const int N = 2e5 + 10;
int n, m;
char s[N];
int a[N];
struct Node {
  int l, r;
  int sum;
  bool b;
} tr[N << 2];

void pushup(int u) {
  tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u) {
  auto &l = tr[u << 1], &r = tr[u << 1 | 1];
  if (tr[u].b) {
	l.sum = l.r - l.l + 1 - l.sum;
	r.sum = r.r - r.l + 1 - r.sum;
	l.b ^= 1;
	r.b ^= 1;
	tr[u].b = false;
  }
}

void build(int u, int l, int r) {
  tr[u] = {l, r, 0, false};
  if (l == r) {
	tr[u].sum = a[l];
	return;
  }

  int mid = l + r >> 1;
  build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
  pushup(u);
}

void modify(int u, int l, int r) {
  if (l <= tr[u].l && tr[u].r <= r) {
	tr[u].sum = tr[u].r - tr[u].l + 1 - tr[u].sum;
	tr[u].b ^= 1;
	return;
  }

  pushdown(u);
  int mid = tr[u].l + tr[u].r >> 1;
  if (l <= mid) modify(u << 1, l, r);
  if (r > mid) modify(u << 1 | 1, l, r);
  pushup(u);
}

int sum(int u, int l, int r) {
  if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
  pushdown(u);
  int mid = tr[u].l + tr[u].r >> 1;
  int res = 0;
  if (l <= mid) res = sum(u << 1, l, r);
  if (r > mid) res += sum(u << 1 | 1, l, r);
  return res;
}

int main() {
  scanf("%d%d", &n, &m);
  scanf("%s", s + 1);
  for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
  build(1, 1, n);
  while (m--) {
	int op, l, r;
	scanf("%d%d%d", &op, &l, &r);
	if (op == 0) modify(1, l, r);
	else printf("%d\n", sum(1, l, r));
  }
}

每次操作时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值