题目大意,给你一串灯,按一下开关可以将灯的状态取反(开变成关,关变成开)。维护这个序列的两种操作:询问区间内有多少灯是开着的,区间按灯。
开始想的是分别维护区间内0的数量,1的数量,两个懒标记。后来真正写到维护懒标记的时候却感觉不太可行,因为你并不精确的知道区间内哪里是开着,哪里是关着的。
其实我们本质上就是在维护整个异或序列。因为把每个位置取反,实际上就是在进行异或运算。(0->1,1->0)。这样我们在区间修改的时候,只需要维护区间内1的个数,把它用区间长度减去原来的值便得到新值。而懒标记的维护,直接进行异或运算即可。因为懒标记实际上就是在告诉儿子节点怎么做,传递简单的修改无法维护的信息。所以直接异或即可。
这个问题还有一个加加加加加加强版,bzoj1858,有时间干掉他。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #define maxn 100090 4 5 using namespace std; 6 7 int n,m; 8 struct SegmentTree{ 9 int l,r,len; 10 int val,lazy; 11 }t[maxn*4]; 12 13 void build(int p,int l,int r) 14 { 15 t[p].l=l,t[p].r=r,t[p].len=r-l+1; 16 if(l==r) 17 return ; 18 int mid=(l+r)>>1; 19 build(p*2,l,mid); 20 build(p*2+1,mid+1,r); 21 } 22 23 void update(int p) 24 { 25 if(t[p].l==t[p].r) return ; 26 if(!t[p].lazy) return ; 27 t[p*2].val=t[p*2].len-t[p*2].val; 28 t[p*2+1].val=t[p*2+1].len-t[p*2+1].val; 29 t[p*2].lazy^=1; 30 t[p*2+1].lazy^=1; 31 t[p].lazy=0; 32 } 33 34 void change(int p,int l,int r) 35 { 36 update(p); 37 if(t[p].l==l&&t[p].r==r) 38 { 39 t[p].lazy^=1; 40 t[p].val=t[p].len-t[p].val; 41 return ; 42 } 43 int mid=(t[p].l+t[p].r)>>1; 44 if(l>mid) change(p*2+1,l,r); 45 else if(r<=mid) change(p*2,l,r); 46 else change(p*2,l,mid),change(p*2+1,mid+1,r); 47 t[p].val=t[p*2].val+t[p*2+1].val; 48 } 49 50 int ask(int p,int l,int r) 51 { 52 update(p); 53 if(t[p].l==l&&t[p].r==r) return t[p].val; 54 int mid=(t[p].l+t[p].r)>>1; 55 if(l>mid) return ask(p*2+1,l,r); 56 else if(r<=mid) return ask(p*2,l,r); 57 else return ask(p*2,l,mid)+ask(p*2+1,mid+1,r); 58 } 59 60 int main() 61 { 62 scanf("%d%d",&n,&m); 63 build(1,1,n); 64 for(int i=1;i<=m;i++) 65 { 66 int opt=0,l=0,r=0; 67 scanf("%d%d%d",&opt,&l,&r); 68 if(opt==0) 69 change(1,l,r); 70 else if(opt==1) 71 printf("%d ",ask(1,l,r)); 72 } 73 return 0; 74 }