1. 程式人生 > >P3870 [TJOI2009]開關

P3870 [TJOI2009]開關

HA () 修改 names -s etc art $0 clas

題目描述

現有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

Solution:

  本題裸的線段樹模板~~。

  對於每次修改取反,直接修改一下懶惰標記(改為$Xor\;1$),並對區間和取反(即$sum[rt]=(r-l+1)-sum[rt]$),意味著原來的$0$的個數就是修改後區間中的$1$的個數。

  然後就是簡單的區間修改區間求和拉~。

代碼: 

 1 #include<bits/stdc++.h>
 2 #define il inline
 3 #define lson l,m,rt<<1
 4 #define rson m+1,r,rt<<1|1
 5 using namespace std;
 6 const int N=1e5+5;
 7 int n,m,sum[N<<2],lazy[N<<2];
 8 il int gi(){
 9     int a=0;char x=getchar();bool f=0;
10     while((x<
0||x>9)&&x!=-)x=getchar(); 11 if(x==-)x=getchar(),f=1; 12 while(x>=0&&x<=9)a=a*10+x-48,x=getchar(); 13 return f?-a:a; 14 } 15 il void pushup(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];} 16 il void pushdown(int rt,int k){ 17 if(lazy[rt]){ 18 lazy[rt<<1]^=1; 19 lazy[rt<<1|1]^=1; 20 sum[rt<<1]=(k-(k>>1))-sum[rt<<1]; 21 sum[rt<<1|1]=(k>>1)-sum[rt<<1|1]; 22 lazy[rt]=0; 23 } 24 } 25 il void update(int L,int R,int c,int l,int r,int rt){ 26 if(L<=l&&R>=r){lazy[rt]^=1;sum[rt]=(r-l+1)-sum[rt];return;} 27 pushdown(rt,r-l+1); 28 int m=l+r>>1; 29 if(L<=m)update(L,R,c,lson); 30 if(m<R)update(L,R,c,rson); 31 pushup(rt); 32 } 33 il int query(int L,int R,int l,int r,int rt){ 34 if(L<=l&&R>=r){return sum[rt];} 35 pushdown(rt,r-l+1); 36 int m=l+r>>1,ret=0; 37 if(L<=m)ret+=query(L,R,lson); 38 if(m<R)ret+=query(L,R,rson); 39 return ret; 40 } 41 int main(){ 42 n=gi(),m=gi(); 43 int x,y,z; 44 while(m--){ 45 x=gi(),y=gi(),z=gi(); 46 if(!x)update(y,z,1,1,n,1); 47 else printf("%d\n",query(y,z,1,n,1)); 48 } 49 return 0; 50 }

 

P3870 [TJOI2009]開關