当线段树遇上无敌位运算!
还是老套路,线段树维护区间和,一个区间有几个"1"就是这个区间的区间和,同时支持区间修改区间查询,只不过操作从加法变成了异或。主要难点就在更新懒标记那里,详解见代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXX=200010;
int read()
{
int ans=0;
char ch=getchar(),last=' ';
while(ch>'9'||ch<'0')
{last=ch;ch=getchar();}
while(ch>='0'&&ch<='9')
{ans=ans*10+ch-'0';ch=getchar();}
if(last=='-')ans=-ans;
return ans;
}
int n,m,a[MAXX];
char c[MAXX];
struct Segment_Tree{
int l,r,sum,tag;
}t[4*MAXX];
void pushup(int p)
{
t[p].sum=t[p*2].sum+t[p*2+1].sum;
//维护区间和,也就是统计区间里有几个"1"
}
void pushdown(int p)//下放懒标记
{
if(t[p].tag){
t[p*2].tag^=1;
t[p*2+1].tag^=1;
//更新子节点的懒标记
t[p*2].sum=(t[p*2].r-t[p*2].l+1)-t[p*2].sum;
//该区间异或1后全部取反,因此"1"的数量等于整个区间长减去原来"1"的数量
t[p*2+1].sum=(t[p*2+1].r-t[p*2+1].l+1)-t[p*2+1].sum;
t[p].tag=0;
}
}
void build(int p,int l,int r)
{
t[p].l=l;t[p].r=r;
if(l==r){t[p].sum=a[l];t[p].tag=0;return;}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
pushup(p);
}
void Xor(int p,int l,int r)
{
if(l<=t[p].l&&r>=t[p].r)
{t[p].sum=(t[p].r-t[p].l+1)-t[p].sum;
t[p].tag^=1;return;}
pushdown(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)Xor(p*2,l,r);
if(r>mid)Xor(p*2+1,l,r);
pushup(p);
}
int ask(int p,int l,int r)
{
if(l<=t[p].l&&r>=t[p].r)return t[p].sum;
pushdown(p);
int mid=(t[p].l+t[p].r)/2;
int ans=0;
if(l<=mid)ans+=ask(p*2,l,r);
if(r>mid)ans+=ask(p*2+1,l,r);
return ans;
}
int main()
{
n=read();m=read();
cin>>c+1;
for(int i=1;i<=n;i++)
a[i]=c[i]-'0';
build(1,1,n);
for(int i=1;i<=m;i++){
int op,l,r;
op=read();l=read();r=read();
if(op==0)Xor(1,l,r);
else printf("%d
",ask(1,l,r));
}
return 0;
}