题目描述 Description
YYX家门前的街上有N(2<=N<=100000)盏路灯,在晚上六点之前,这些路灯全是关着的,六点之后,会有M(2<=m<=100000)个人陆续按下开关,这些开关可以改变从第i盏灯到第j盏灯的状态,现在YYX想知道,从第x盏灯到第y盏灯中有多少是亮着的(1<=i,j,x,y<=N)
输入描述 Input Description
第 1 行: 用空格隔开的两个整数N和M
第 2..M+1 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号(0代表按下开关,1代表询问状态), x 和 y
输出描述 Output Description
第 1..询问总次数 行:对于每一次询问,输出询问的结果
样例输入 Sample Input
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
样例输出 Sample Output
1
2
数据范围及提示 Data Size & Hint
一共4盏灯,5个操作,下面是每次操作的状态(X代表关上的,O代表开着的):
XXXX -> OOXX -> OXOO -> 询问1~3 -> OOXX -> 询问1~4
解题报告
这道题的操作就区间取反+区间求和,用线段树实现。线段树的每一个点存储这段的和以及懒标记。其中懒标记的更新是nd->flag=!nd->flag(取反),而每一次求和应该是这段区间的长度减去原来的和(nd->sum=(rg-lf+1)-nd->sum)。
代码如下:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100000;
int n,m;
int a[N+5];
struct node
{
int sum,flag;
node *lson,*rson;
void pushdown(int lf,int rg)
{
if(flag)
{
int mid=(lf+rg)>>1;
lson->flag=!lson->flag;
lson->sum=(mid-lf+1)-lson->sum;
rson->flag=!rson->flag;
rson->sum=(rg-mid)-rson->sum;
flag=0;
}
}
void update()
{
sum=lson->sum+rson->sum;
}
};
struct node dizhi[4*N+5],*tail=dizhi,*root;
node *build(int lf,int rg)
{
node *nd=++tail;
if(lf==rg)
{
nd->sum=0;
nd->flag=0;
}
else
{
int mid=(lf+rg)>>1;
nd->lson=build(lf,mid);
nd->rson=build(mid+1,rg);
nd->sum=nd->lson->sum+nd->rson->sum;
nd->flag=0;
}
return nd;
}
void modify(node *nd,int lf,int rg,int L,int R)
{
if(L<=lf&&rg<=R)
{
nd->sum=(rg-lf+1)-nd->sum;
nd->flag=!nd->flag;
return ;
}
nd->pushdown(lf,rg);
int mid=(lf+rg)>>1;
if(L<=mid)modify(nd->lson,lf,mid,L,R);
if(R>mid)modify(nd->rson,mid+1,rg,L,R);
nd->update();
}
int query(node *nd,int lf,int rg,int L,int R)
{
if(L<=lf&&rg<=R)return nd->sum;
int mid=(lf+rg)>>1;
nd->pushdown(lf,rg);
long long rt=0;
if(L<=mid)rt+=query(nd->lson,lf,mid,L,R);
if(R>mid)rt+=query(nd->rson,mid+1,rg,L,R);
nd->update();
return rt;
}
int main()
{
scanf("%d%d",&n,&m);
root=build(1,n);
while(m--)
{
int opt,lf,rg;
scanf("%d%d%d",&opt,&lf,&rg);
if(!opt)modify(root,1,n,lf,rg);
else printf("%d\n",query(root,1,n,lf,rg));
}
return 0;
}