標籤:
描述http://codevs.cn/problem/1690/
一排共 n 個燈,起初都是關著的,現在有 m 次操作. 0 開頭的操作代表將 l ~ r 的開關按一遍,1 開頭的操作代表詢問 l ~ r 共多少個開著的燈.
分析
剛開始想的是用 0 , 1 , -1 分別代表區間都是關著的,都是開著的,有開的又有關的,跟新和查詢的時候都是尋找到非 -1 的區間再做處理,但是 TLE 了.
想一下如果燈是 1 0 1 0 1 0 1 0 1...這樣排列的,那就退化得不成樣子了.
所以用 on 和 off 分別代表區間內開著的和關著的等的數量,如果按開關,就把這兩個值交換.
最後可以不用 off ,只用一個 on ,可以通過區間長度進行計算.
#include<cstdio>const int maxn=100005;struct node { int l,r; int on;bool d; }a[3*maxn];int n,m;void build_tree(int l,int r,int k){ a[k].l=l; a[k].r=r; a[k].on=0; a[k].d=false; if(l==r) return; int mid=l+(r-l)/2; build_tree(l,mid,2*k); build_tree(mid+1,r,2*k+1);}inline void turn(int k){ a[k].on=(a[k].r-a[k].l+1)-a[k].on; a[k].d^=true;}void update(int l,int r,int k){ if(a[k].l==l&&a[k].r==r) { turn(k); return; } if(a[k].d) { turn(2*k); turn(2*k+1); a[k].d=false; } int mid=a[k].l+(a[k].r-a[k].l)/2; if(r<=mid) update(l,r,2*k); else if(l>mid) update(l,r,2*k+1); else { update(l,mid,2*k); update(mid+1,r,2*k+1); } a[k].on=a[2*k].on+a[2*k+1].on;}int search(int l,int r,int k){ if(a[k].l==l&&a[k].r==r) return a[k].on; if(a[k].d) { turn(2*k); turn(2*k+1); a[k].d=false; } int mid=a[k].l+(a[k].r-a[k].l)/2; if(r<=mid) return search(l,r,2*k); else if(l>mid) return search(l,r,2*k+1); else return search(l,mid,2*k)+search(mid+1,r,2*k+1);}int main(){ scanf("%d%d",&n,&m); build_tree(1,n,1); int qry,l,r; for(int i=1;i<=m;i++) { scanf("%d%d%d",&qry,&l,&r); switch(qry) { case 0: update(l,r,1); break; case 1: printf("%d\n",search(l,r,1)); break; } } return 0;}
Codevs_1690_開關燈(線段樹)