线段树懒标记的总结+运用 XOR的艺术(洛谷 P2574)

XOR的艺术

题目描述

AKN 觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下

拥有一个伤害串,是一个长度为 n 的只含字符 0 和字符 1 的字符串。规定这个字符串的首字符是第一个字符,即下标从 1 开始。

给定一个范围 [l, r],伤害为伤害串的这个范围内中字符 1 的个数

会修改伤害串中的数值,修改的方法是把 [l, r] 中所有原来的字符 0 变成 1,将 1 变成 0。

AKN 想知道一些时刻的伤害,请你帮助他求出这个伤害。

输入格式

输入的第一行有两个用空格隔开的整数,分别表示伤害串的长度 n,和操作的个数 m。

输入第二行是一个长度为 n 的字符串 S,代表伤害串。

第 3 到第 (m+2) 行,每行有三个用空格隔开的整数 op,l,r。代表第 i 次操作的方式和区间,规则是:

若 op=0,则表示将伤害串的 [l, r] 区间内的 0 变成 1,1 变成 0。
若 op=1,则表示询问伤害串的 [l, r] 区间内有多少个字符 1。

输出格式

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


这道题的考察的就是对线段树懒标记的理解和运用;

这里刚好复习一下;

懒标记一般是区间修改才会用到的;

懒标记顾名思义,就是一个特别懒的东西,只有在你需要它的时候,它才会出现;当我们对区间修改时,如果一个个的修改,那么复杂度爆炸,显然不可以;这时懒标记的作用就体现了,对每个区间加一个懒标记,标志着这个区间是否进行了修改,如果进行了,那么它的子区间也要进行修改,并且把懒标记转给子结点(因为子结点的子区间也要修改);

这道题不仅体现了懒标记的用处,还说明了懒标记可能会产生冲突;也就是说上一个操作的标记还没处理完,这个操作的标记又来了;

说回这道题的解法:对每个区间的懒标记进行异或操作,因为操作两次相当于没操作,所以异或特别适合;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200010;
const int M=2000100;
const LL mod=1e8-3;
int n,m;
string s;
struct Node{
	int l,r,w,lt;	
}tr[N*4];
void build(int l,int r,int k){
	tr[k].l=l,tr[k].r=r;
	if(l==r) return;
	int d=(l+r)>>1;
	build(l,d,ls);
	build(d+1,r,rs);
}
void add(int p,int k){
	if(tr[k].l==tr[k].r){
		tr[k].w++;
		return;
	}
	int d=(tr[k].l+tr[k].r)>>1;
	if(p<=d) add(p,ls);
	else add(p,rs);
	tr[k].w=tr[ls].w+tr[rs].w;
}
void pd(int k){
	tr[ls].lt^=1;
	tr[rs].lt^=1;
	tr[ls].w=tr[ls].r-tr[ls].l+1-tr[ls].w;
	tr[rs].w=tr[rs].r-tr[rs].l+1-tr[rs].w;
	tr[k].lt=0;
}
void update(int l,int r,int k){
	if(tr[k].l>=l&&tr[k].r<=r){
		tr[k].w=tr[k].r-tr[k].l+1-tr[k].w;
		tr[k].lt^=1;
		return;
	}
	if(tr[k].lt) pd(k);
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) update(l,r,ls);
	if(r>d) update(l,r,rs);
	tr[k].w=tr[ls].w+tr[rs].w;
}
int query(int l,int r,int k){
	if(tr[k].l>=l&&tr[k].r<=r) return tr[k].w;
	if(tr[k].lt) pd(k);
	int sum=0;
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) sum+=query(l,r,ls);
	if(r>d) sum+=query(l,r,rs);
	return sum;
} 
int main(){
	cin>>n>>m;
	cin>>s;
	build(1,n,1);
	for(int i=0;i<n;i++) if(s[i]=='1') add(i+1,1);
	while(m--){
		int p,l,r;
		scanf("%d%d%d",&p,&l,&r);
		if(p==0) update(l,r,1);
		else printf("%d\n",query(l,r,1));
	}
	return 0;
}

发布了264 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44291254/article/details/105210323