线段树维护区间01

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


Description

晚上到家小 W 通过开关灯来保持自己神经的兴奋以便清醒地理笔记。N(2N100,000)盏灯被连续的编号为 1N。刚回到家的时候,所有的灯都是关闭的。 
小W 通过 N个按钮来控制灯的开关, 按第 i 个按钮可以改变第 i 盏灯的状态。 
小W 发出 M (1M100,000)条指令,每个指令都是两个整数中的一个(0 或 1)。 
第1 种指令(用 0 表示)包含两个数字 SiSi 和 Ei(1SiEiN),它们表示起始开关和终止开关。小 W 需要把从 Si 到 Ei之间的按钮都按一次,就可以完成这个指令。 
第2 种指令(用 1 表示)同样包含两个数字 Si 和 Ei(1SiEiN),不过这种指令是询问从 Si 到 Ei 之间的灯有多少是亮着的。 

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

Input

第1 行: 用空格隔开的两个整数 N 和 M。 
第2..M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号, Si 和 Ei。 

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% 的数据: 1N,M100
  • 对于 40% 的数据: 1N,M10000
  • 对于另外 30% 的数据: 只有最后一组是 1 指令,前 M-1 组为 0 指令。
  • 对于 100% 的数据: 1N,M100000

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 ;
    } 

这样有一个问题,就是

 就是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));
        }
    }
    
}
 
原文地址:https://www.cnblogs.com/lipu123/p/14305772.html