线段树区间异或+区间和并+查询最区间大连续1的个数
Posted liuzuolin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树区间异或+区间和并+查询最区间大连续1的个数相关的知识,希望对你有一定的参考价值。
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3911
题意:给定序列(只有0,1),修改是将0变成1,1变成0,询问是查询区间最大连续1的数目。
用线段树维护7个变量:
第1,2个是区间的最大前缀0,和前缀1
第3,4个是区间的最大后缀0,和后缀1
第5,6个是区间的最大连续0,和最大连续1
第7个是lazy(区间更新必备)
由于我们存了1,0的相关值,在向下更改时只需将1,0的有关值全部交换即可。
而在询问时要注意询问区间在树上被分割时要计算左边连续1与右边连续1的最大和中间合并(左边后面最大1与右边前面最大1)的值再取最大。
剩下的就是细心了。
代码:
1 ///http://acm.hdu.edu.cn/showproblem.php?pid=3911 2 #include<iostream> 3 #include<cstdio> 4 #include<string.h> 5 using namespace std; 6 const int maxn=1e5+5; 7 struct Tree{ 8 int l0,r0; 9 int l1,r1; 10 int ml0,ml1; 11 int lazy; 12 }tree[maxn<<2]; 13 int val; 14 void up(int k,int l,int r){ 15 int mid=(l+r)>>1; 16 ///前缀最大1的个数 17 tree[k].l1=tree[k<<1].l1; 18 if(tree[k].l1==mid-l+1){ 19 tree[k].l1+=tree[k<<1|1].l1; 20 } 21 ///前缀最大0的个数 22 tree[k].l0=tree[k<<1].l0; 23 if(tree[k].l0==mid-l+1){ 24 tree[k].l0+=tree[k<<1|1].l0; 25 } 26 ///后缀最大1的个数 27 tree[k].r1=tree[k<<1|1].r1; 28 if(tree[k].r1==r-mid){ 29 tree[k].r1+=tree[k<<1].r1; 30 } 31 ///后缀最大0的个数 32 tree[k].r0=tree[k<<1|1].r0; 33 if(tree[k].r0==r-mid){ 34 tree[k].r0+=tree[k<<1].r0; 35 } 36 ///区间最大1和最大0的个数 37 tree[k].ml1=max(max(tree[k<<1].ml1,tree[k<<1|1].ml1),tree[k<<1].r1+tree[k<<1|1].l1); 38 tree[k].ml0=max(max(tree[k<<1].ml0,tree[k<<1|1].ml0),tree[k<<1].r0+tree[k<<1|1].l0); 39 } 40 41 void _swap(int k){ 42 swap(tree[k].l0,tree[k].l1); 43 swap(tree[k].r0,tree[k].r1); 44 swap(tree[k].ml0,tree[k].ml1); 45 } 46 47 void pushdowm(int k,int l,int r){ 48 if(l!=r){ 49 tree[k<<1].lazy^=1; 50 tree[k<<1|1].lazy^=1; 51 _swap(k<<1); 52 _swap(k<<1|1); 53 tree[k].lazy=0; 54 } 55 } 56 57 void build(int k,int l,int r){ 58 tree[k].lazy=0; 59 if(l==r){ 60 scanf("%d",&val); 61 if(val==1){ 62 tree[k].l1=tree[k].r1=tree[k].ml1=1; 63 tree[k].l0=tree[k].r0=tree[k].ml0=0; 64 65 }else{ 66 tree[k].l1=tree[k].r1=tree[k].ml1=0; 67 tree[k].l0=tree[k].r0=tree[k].ml0=1; 68 } 69 return ; 70 } 71 int mid=(l+r)>>1; 72 build(k<<1,l,mid); 73 build(k<<1|1,mid+1,r); 74 up(k,l,r); 75 } 76 77 void update(int k,int l,int r,int L,int R){ 78 if(l>=L&&r<=R){ 79 tree[k].lazy^=1; 80 _swap(k); 81 return ; 82 } 83 if(tree[k].lazy) pushdowm(k,l,r); 84 int mid=(l+r)>>1; 85 if(mid>=R) update(k<<1,l,mid,L,R); 86 else if(mid<L) update(k<<1|1,mid+1,r,L,R); 87 else{ 88 update(k<<1,l,mid,L,R); 89 update(k<<1|1,mid+1,r,L,R); 90 } 91 up(k,l,r); 92 } 93 94 int query(int k,int l,int r,int L,int R){ 95 if(l>=L&&r<=R){ 96 return tree[k].ml1; 97 } 98 if(tree[k].lazy) pushdowm(k,l,r); 99 int mid=(l+r)>>1; 100 if(mid>=R) return query(k<<1,l,mid,L,R); 101 else if(mid<L) return query(k<<1|1,mid+1,r,L,R); 102 else{ 103 int m1 = query(k<<1,l,mid,L,R); 104 int m2 = query(k<<1|1,mid+1,r,L,R); 105 int m3=min(mid-L+1,tree[k<<1].r1); 106 int m4=min(R-mid,tree[k<<1|1].l1); 107 return max(max(m1,m2),m3+m4); 108 } 109 up(k,l,r); 110 } 111 112 113 int main(){ 114 int N; 115 while(scanf("%d",&N)!=EOF){ 116 build(1,1,N); 117 int M,q,L,R; 118 scanf("%d",&M); 119 while(M--){ 120 scanf("%d%d%d",&q,&L,&R); 121 if(q==1){ 122 update(1,1,N,L,R); 123 }else{ 124 printf("%d\n",query(1,1,N,L,R)); 125 } 126 } 127 } 128 return 0; 129 }
以上是关于线段树区间异或+区间和并+查询最区间大连续1的个数的主要内容,如果未能解决你的问题,请参考以下文章
GDKOI2016Day1T1-魔卡少女拆位线段树维护区间内所有连续子区间的异或和
SPOJ GSS4 (区间开根号 + 区间查询) (线段树)