b_lg_XOR的艺术 & 开关(tag标记是否点亮)
现有n盏灯排成一排,有两种操作,一种是对区间中的灯的开关状态进行反转,一种是统计区间中开着的灯的数量(n<=1e5)
思路
tag[k]有两种状态:
- 0:表示线段树中结点k自身及其所管控的子树的灯的状态为关闭
- 1:表示线段树中结点k自身及其所管控的子树的灯的状态为点亮
在计算的时候如果tag[k]为0,则表示他被熄灭了,所以子树也是熄灭的,故push_down可以提前返回了
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,a[N<<2],tag[N<<2];
char s[N];
void p_up(int k) {
a[k]=a[k<<1]+a[k<<1|1];
}
void p_down(int l, int r, int k) {
if(tag[k]==0) return;
int m=l+r>>1;
a[k<<1]=(m-l+1)-a[k<<1];
a[k<<1|1]=(r-m)-a[k<<1|1];
tag[k]^=1, tag[k<<1]^=1, tag[k<<1|1]^=1;
}
void build(int l, int r, int k) {
if (l==r) {
a[k]=tag[k]=s[l]=='1';
return;
}
int m=l+r>>1;
build(l,m,k<<1);
build(m+1,r,k<<1|1);
p_up(k);
}
void upd(int ql, int qr, int l, int r, int k) {
if (ql<=l && r<=qr) {
a[k]=(r-l+1)-a[k]; //[l,r]中灯的总数量,a[k]为[l,r]中亮着的灯的数量
tag[k]^=1;
return;
}
p_down(l,r,k);
int m=l+r>>1;
if (m>=ql) upd(ql,qr,l,m,k<<1);
if (m<qr) upd(ql,qr,m+1,r,k<<1|1);
p_up(k);
}
int ask(int ql, int qr, int l, int r, int k) {
if (ql<=l && r<=qr)
return a[k];
p_down(l,r,k);
int m=l+r>>1, ans=0;
if (m>=ql) ans+=ask(ql,qr,l,m,k<<1);
if (m<qr) ans+=ask(ql,qr,m+1,r,k<<1|1);
return ans;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>s+1;
build(1,n,1);
for (int i=0; i<m; i++) {
int t,l,r; cin>>t>>l>>r;
if (t==0) upd(l,r,1,n,1);
else cout<<ask(l,r,1,n,1)<<'\n';
}
return 0;
}