线段树区间取反

题目描述

现有N(2 ≤ N ≤ 100000)盏灯排成一排,从左到右依次编号为:1,2,......,N。然后依次执行M(1 ≤ M ≤ 100000)项操作,操作分为两种:第一种操作指定一个区间[a, b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开),第二种操作是指定一个区间[a, b],要求你输出这个区间内有多少盏灯是打开的。灯在初始时都是关着的。

输入输出格式

输入格式:

 

第一行有两个整数N和M,分别表示灯的数目和操作的数目。接下来有M行,每行有三个整数,依次为:c, a, b。其中c表示操作的种类,当c的值为0时,表示是第一种操作。当c的值为1时表示是第二种操作。a和b则分别表示了操作区间的左右边界(1 ≤ a ≤ b ≤ N)。

 

输出格式:

 

每当遇到第二种操作时,输出一行,包含一个整数:此时在查询的区间中打开的灯的数目。

 

输入输出样例

输入样例#1
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
输出样例#1
1
2



#include<iostream>
#include<cstdio>
using namespace std;
#define ls(x) (x<<1)
#define rs(x) (ls(x)|1)
const int maxn=1e5+5;
struct FFF{
    int l,r;
    int op;
	int cl;
	bool rev;
    int mid(){return l+r>>1;}
    int len(){return r-l+1;}
}t[maxn<<2];
int n,m;
void up(int o){
	t[o].op=t[ls(o)].op+t[rs(o)].op;
	t[o].cl=t[ls(o)].cl+t[rs(o)].cl;
}
void build(int l=1,int r=n,int o=1){
    t[o].l=l;t[o].r=r;
	t[o].op=0;
    if(l==r){
        t[o].cl=1;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,ls(o));
    build(mid+1,r,rs(o));
    up(o);
}
void down(int o){
    if(t[o].rev){
		for(int i=0;i<=1;++i){
			t[ls(o)|i].rev^=1;
			swap(t[ls(o)|i].op,t[ls(o)|i].cl);
		}
		t[o].rev=0;
	}
}
void add(int l,int r,int o=1){
    if(l<=t[o].l&&t[o].r<=r){
        t[o].rev^=1;
		swap(t[o].op,t[o].cl);
        return;
    }
    int mid=t[o].mid();
    down(o);
    if(l<=mid)add(l,r,ls(o));
    if(r>mid)add(l,r,rs(o));
    up(o);
}
int getsum(int l,int r,int o=1){
    if(l<=t[o].l&&t[o].r<=r){
        return t[o].op;
    }
    int ans=0;
    int mid=t[o].mid();
    down(o);
    if(l<=mid)ans+=getsum(l,r,ls(o));
    if(r>mid)ans+=getsum(l,r,rs(o));
    return ans;
}
signed main(){
    cin>>n>>m;
	build();
	while(m--){
		int op,l,r;
		cin>>op>>l>>r;
		if(op==0){
			add(l,r);
		}
		if(op==1){
			cout<<getsum(l,r)<<endl;
		}
	}
    return 0;
}

  

posted @ 2018-08-05 14:05  kraylas  阅读(1005)  评论(2编辑  收藏  举报