洛谷P2574 XOR的艺术(线段树)——Chemist

当线段树遇上无敌位运算!

还是老套路,线段树维护区间和,一个区间有几个"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;
}
原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9374039.html