线段树维护区间01

G. 小 W 开关灯 Problem 4467  ] Discussion ]


Description

晚上到家小 W 通过开关灯来保持自己神经的兴奋以便清醒地理笔记。 之间的灯有多少是亮着的。 

请你帮助小 W 得到正确的答案。 

Input

第1 行: 用空格隔开的两个整数 。 

Output

对于每一次询问, 输出一行表示询问的结果。

Samples

Input Copy
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
Output
1
2

Hint

【样例解释】
一共有 4 盏灯, 5 个指令。 下面是执行的情况:
1 2 3 4
 

Init: O O O O O = 关 * = 开
0 1 2 -> * * O O 改变灯 1 和 2 的状态
0 2 4 -> * O * *

1 2 3 -> 1 输出在 2..3 的范围内有多少灯是亮的
0 2 4 -> * * O O 改变灯 2 ,3 和 4 的状态
1 1 4 -> 2 输出在 1..4 的范围内有多少灯是亮的

【数据规模】

  • 对于 20% 的数据: 
  • 对于 40% 的数据: 
  • 对于另外 30% 的数据: 只有最后一组是 1 指令,前 M-1 组为 0 指令。
  • 对于 100% 的数据: 

Source

石光中学 2018泉州集训提高组day4
这个题一看就是要用线段树做
主要是要知道一个知识 t[p].s=t[p].r-t[p].l+1-t[p].s;(就是区间长度为5,有两个2,t[p].s=2,如果反转的话,t[p].s=5-2=3)
这个就是区间变化之后怎么区间和
然后在弄一个区间懒惰标记判断要不要下传标记,就是区间是不是要变换
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename Tp>
void read(Tp &x){
    x=0;char ch=1;int fh;
    while(ch!='-'&&(ch>'9'||ch<'0')){
        ch=getchar();
    }
    if(ch=='-'){
        fh=-1;ch=getchar();
    }else fh=1;
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
    }
    x*=fh;
}
inline char read1()
{
    register char ch=getchar();
    while(ch<'A'||ch>'M')ch=getchar();
    return ch;
}
const int maxn=5e6+100; 
struct node{
    int l,r;
    int s;
    int lazy;
}t[maxn];
void build(int p,int l,int r){
    t[p].l=l;
    t[p].r=r;
    if(l==r){
        return ;
    }
    int mid=(l+r)/2;
    build(2*p,l,mid);
    build(2*p+1,mid+1,r);
} 
void push(int p){
    if(t[p].lazy){
        t[2*p].s=(t[2*p].r-t[2*p].l+1)-t[2*p].s;
        t[2*p+1].s=(t[2*p+1].r-t[2*p+1].l+1)-t[2*p+1].s;
        t[2*p].lazy^=1;
        t[2*p+1].lazy^=1;
        t[p].lazy=0;
    } 
}
void update(int p,int l,int r){ 
    if(t[p].l>=l&&t[p].r<=r){ 
        t[p].s=t[p].r-t[p].l+1-t[p].s;
        t[p].lazy^=1;
        return ;
    } 
    if(t[p].lazy) push(p);
    int mid=(t[p].l+t[p].r)/2; 
    if(l<=mid){
        update(2*p,l,r); 
    }
    if(r>mid){
        update(2*p+1,l,r);
    }
    t[p].s=t[2*p].s+t[2*p+1].s; 
} 
int query(int p,int l,int r){
    if(t[p].l>=l&&t[p].r<=r){
        return t[p].s;
    }
    int ans=0;
    if(t[p].lazy) push(p);
    int mid=(t[p].l+t[p].r)/2; 
    if(l<=mid){
        ans+=query(2*p,l,r);
    }
    if(r>mid){
        ans+=query(2*p+1,l,r);
    }
    return ans;
}
int n,m;
int main(){
    cin>>n>>m;
    int op,x,y;
    build(1,1,n); 
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&x,&y);
        if(op==0){
            update(1,x,y);
        }
        else if(op==1){
            printf("%d
",query(1,x,y));
        }
    }
    
}
 一开始wa了好几遍是这样写的,就是没有异或1,而是变化一次就把他变成一
void push(int p){
    if(t[p].lazy){
        t[2*p].s=(t[2*p].r-t[2*p].l+1)-t[2*p].s;
        t[2*p+1].s=(t[2*p+1].r-t[2*p+1].l+1)-t[2*p+1].s;
        t[2*p].lazy=1;
        t[2*p+1].lazy=1;
        t[p].lazy=0;
    } 
}
void update(int p,int l,int r){ 
    int L=t[p].l,R=t[p].r;
    if(L>=l&&R<=r){ 
        t[p].s=(R-L+1)-t[p].s;
        t[p].lazy=1;
        return ;
    } 

这样有一个问题,就是

线段树维护区间01

 就是1-2区间变化两次,而这个区间是一整个节点没有在update中下传标记,在询问中下传标记时下传了一次,

本来该没有变化的,就是如果用异或的话询问两次的话,就变成0了,就不会出现上面的情况了

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename Tp>
void read(Tp &x){
    x=0;char ch=1;int fh;
    while(ch!='-'&&(ch>'9'||ch<'0')){
        ch=getchar();
    }
    if(ch=='-'){
        fh=-1;ch=getchar();
    }else fh=1;
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
    }
    x*=fh;
}
inline char read1()
{
    register char ch=getchar();
    while(ch<'A'||ch>'M')ch=getchar();
    return ch;
}
const int maxn=5e6+100; 
struct node{
    int l,r;
    int s;
    int lazy;
}t[maxn];
void build(int p,int l,int r){
    t[p].l=l;
    t[p].r=r;
    if(l==r){
        return ;
    }
    int mid=(l+r)/2;
    build(2*p,l,mid);
    build(2*p+1,mid+1,r);
} 
void push(int p){
    if(t[p].lazy){
        t[2*p].s=(t[2*p].r-t[2*p].l+1)-t[2*p].s;
        t[2*p+1].s=(t[2*p+1].r-t[2*p+1].l+1)-t[2*p+1].s;
        t[2*p].lazy^=1;
        t[2*p+1].lazy^=1;
        t[p].lazy=0;
    } 
}
void update(int p,int l,int r){ 
    if(t[p].l>=l&&t[p].r<=r){ 
        t[p].s=t[p].r-t[p].l+1-t[p].s;
        t[p].lazy^=1;
        return ;
    } 
    if(t[p].lazy) push(p);
    int mid=(t[p].l+t[p].r)/2; 
    if(l<=mid){
        update(2*p,l,r); 
    }
    if(r>mid){
        update(2*p+1,l,r);
    }
    t[p].s=t[2*p].s+t[2*p+1].s; 
} 
int query(int p,int l,int r){
    if(t[p].l>=l&&t[p].r<=r){
        return t[p].s;
    }
    int ans=0;
    if(t[p].lazy) push(p);
    int mid=(t[p].l+t[p].r)/2; 
    if(l<=mid){
        ans+=query(2*p,l,r);
    }
    if(r>mid){
        ans+=query(2*p+1,l,r);
    }
    return ans;
}
int n,m;
int main(){
    cin>>n>>m;
    int op,x,y;
    build(1,1,n); 
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&x,&y);
        if(op==0){
            update(1,x,y);
        }
        else if(op==1){
            printf("%d
",query(1,x,y));
        }
    }
    
}