描述
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; }
时间: 2024-10-09 03:51:07