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

洛谷 P3870 [TJOI2009]開關

題目描述

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

思路:還是一道線段樹區間異或,思路跟之前做的洛谷P2574和洛谷P2846完全一樣。

程式碼:

#include<cstdio>
#include<cctype>
#define maxn 100007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int n,m,sum[maxn<<2],lazy[maxn<<2];
inline int qread() {
  char c=getchar();int num=0,f=1;
  for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
  for(;isdigit(c);c=getchar()) num=num*10+c-'0';
  return num*f;
}
inline void pushup(int rt) {
  sum[rt]=sum[ls]+sum[rs];
}
inline void pushdown(int rt, int len) {
  if(lazy[rt]) {
    lazy[ls]^=1;
    lazy[rs]^=1;
    sum[ls]=(len-(len>>1))-sum[ls];
    sum[rs]=(len>>1)-sum[rs];
    lazy[rt]=0;
  }
}
void modify(int rt, int l, int r, int L, int R) {
  if(L>r||R<l) return;
  if(L<=l&&r<=R) {
    lazy[rt]^=1;
    sum[rt]=r-l+1-sum[rt];
    return;
  }
  pushdown(rt,r-l+1);
  int mid=(l+r)>>1;
  modify(ls,l,mid,L,R),modify(rs,mid+1,r,L,R);
  pushup(rt);
}
int csum(int rt, int l, int r, int L, int R) {
  if(L>r||R<l) return 0;
  if(L<=l&&r<=R) return sum[rt];
  pushdown(rt,r-l+1);
  int mid=(l+r)>>1;
  return csum(ls,l,mid,L,R)+csum(rs,mid+1,r,L,R);
}
int main() {
  n=qread(),m=qread();
  for(int i=1,k,l,r;i<=m;++i) {
    k=qread(),l=qread(),r=qread();
    if(!k) modify(1,1,n,l,r);
    else printf("%d\n",csum(1,1,n,l,r));
  }
  return 0;
}