这是一道非常好的线段树区间问题,这道题的代码量偏多,但是用作找BUG也是很不错,于是乎有了:
这对于一个ACMer不是一件多么尴尬的事情,于是,我把这道题做了两遍,反复的思考了这道题后对这方面题也算有了个深刻的认识了:接下来,我会讲解一下我的思路。
题面:
Problem Description
There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she want to know the longest period of consecutive black stones in a range [i, j].
Input
There are multiple cases, the first line of each case is an integer n(1<= n <= 10^5), followed by n integer 1 or 0(1 indicates black stone and 0 indicates white stone), then is an integer M(1<=M<=10^5) followed by M operations formatted as x i j(x = 0 or 1) , x=1 means change the color of stones in range[i,j], and x=0 means ask the longest period of consecutive black stones in range[i,j]
Output
When x=0 output a number means the longest length of black stones in range [i,j].
Sample Input
4 1 0 1 0 5 0 1 4 1 2 3 0 1 4 1 3 3 0 4 4
Sample Output
1 2 0
Source
2011 Multi-University Training Contest 8 - Host by HUST
思路:
这道题大体思路就是一个{ Set(l, r, x),Query(l, r) }的问题,我们可以利用lazy标记来往下递归,假如我们要把这一串点做一个反转,那么把它包含的区间直接钉上lazy标记,然后可以知道若是第二次访问到这个点且刚好包含(只要包含就行),那么lazy标记相互抵消;但若是第二次访问到这个点,且不是包含关系,我们就可以pushdown()了。——按照这个思路,我们就可以继续往下更新了,以上是更新操作的难点,下面讲一下查找操作的几个注意事项。
对于查找操作,我们不难想到可以通过包含关系直接返回之类的,但这里会有些小细节,譬如说我们在处理了ql>=mid+1以及qr<=mid的这两类情况后,剩下的就是mid在ql和qr之间的事了,对于这种情况,我们要考虑三点:
(一)、我们所需要的点是mid两旁的值,也就是答案在中间的情况,这个情况下,我们就需要考虑最远达到距离(mid-ql+1与trie[rt<<1].rs(左半边) 、qr-mid与trie[rt<<1|1].ls(右半边))与最长单边连续区间长度之间的关系,我们只能取小值,不能超出范围。
(二)、若是中间的情况并非最值,那么我们还得考虑两边的区间,譬如左半边是否存在这样的连续区间,它的长度要更加的长。
(三)、同理,我们依然得对右区间求最长连续长度。
第一次AC的代码(多亏学长给我找出的BUG)——有很详细的注释:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#define lowbit(x) ( x&(-x) )
using namespace std;
typedef long long ll;
const int maxN=100010;
struct node
{
int l,r; //左、右端
int ls,rs,ms; //'1'区间的最大
int lz,rz,mz; //'0'区间的最大
}trie[maxN<<2];
int lazy[maxN<<2]={0}; //一开始并没有翻转,若遇到两次翻转,则可以抵消
int a[maxN];
int N,M;
void buildTree(int rt, int l, int r) //建树,这里的建树与更新差不多,因为最初就有断点区间
{
if(l==r) //安排好条件即返回
{
trie[rt].l=l; //更新左右端点
trie[rt].r=r;
trie[rt].ls=trie[rt].rs=trie[rt].ms=a[l]; //‘1’连续点的长度
trie[rt].lz=trie[rt].rz=trie[rt].mz=1-a[l]; //同时也要更新‘0’的连续长度,用作之后Lazy标记访问到后的直接交换
return;
}
trie[rt].l=l; //更新左右端点
trie[rt].r=r;
int mid=(l+r)>>1;
buildTree(rt<<1, l, mid); //先往下递归,后寻找值
buildTree(rt<<1|1, mid+1, r);
trie[rt].ls=trie[rt<<1].ls; //先把左儿子的左端赋给父节点的左端
trie[rt].rs=trie[rt<<1|1].rs; //把右儿子的右端赋给父节点的右端
trie[rt].lz=trie[rt<<1].lz; //'0'节点求长
trie[rt].rz=trie[rt<<1|1].rz;
trie[rt].ms=max(trie[rt<<1].ms, trie[rt<<1|1].ms); //!!!这里错了很多次——要改的应该是左右儿子的最长区间!!!
trie[rt].ms=max(trie[rt].ms, trie[rt<<1].rs+trie[rt<<1|1].ls); //再求加上中间后的最长连续
trie[rt].mz=max(trie[rt<<1].mz, trie[rt<<1|1].mz); //‘0’节点求长
trie[rt].mz=max(trie[rt].mz, trie[rt<<1].rz+trie[rt<<1|1].lz);
if(trie[rt].ls==trie[rt<<1].r-trie[rt<<1].l+1) //左边是满的话,就可以往右边加了
{
trie[rt].ls+=trie[rt<<1|1].ls;
}
if(trie[rt].rs==trie[rt<<1|1].r-trie[rt<<1|1].l+1) //右边也是满的话
{
trie[rt].rs+=trie[rt<<1].rs;
} //同理可得‘0’节点
if(trie[rt].lz==trie[rt<<1].r-trie[rt<<1].l+1) //左边是满的话,就可以往右边加了
{
trie[rt].lz+=trie[rt<<1|1].lz;
}
if(trie[rt].rz==trie[rt<<1|1].r-trie[rt<<1|1].l+1) //右边也是满的话
{
trie[rt].rz+=trie[rt<<1].rz;
}
}
void workdown(int rt)
{
swap(trie[rt].ls, trie[rt].lz);
swap(trie[rt].rs, trie[rt].rz);
swap(trie[rt].ms, trie[rt].mz);
}
void pushdown(int rt, int l, int r)
{
if(lazy[rt])
{
lazy[rt<<1]^=1;
lazy[rt<<1|1]^=1;
workdown(rt<<1);
workdown(rt<<1|1);
lazy[rt]=0;
}
}
void update(int rt, int l, int r, int ql, int qr)
{
if(ql<=l && qr>=r)
{
lazy[rt]^=1;
workdown(rt);
return;
}
pushdown(rt, l, r); //遇到非包含区间,往下递推
int mid=(l+r)>>1;
if(ql<=mid) update(rt<<1, l, mid, ql, qr);
if(qr>=mid+1) update(rt<<1|1, mid+1, r, ql, qr); //下面就是更新了
trie[rt].ls=trie[rt<<1].ls;
trie[rt].rs=trie[rt<<1|1].rs;
trie[rt].lz=trie[rt<<1].lz; //'0'节点求长
trie[rt].rz=trie[rt<<1|1].rz;
trie[rt].ms=max(trie[rt<<1].ms, trie[rt<<1|1].ms); //!!!这里错了很多次——要改的应该是左右儿子的最长区间!!!
trie[rt].ms=max(trie[rt].ms, trie[rt<<1].rs+trie[rt<<1|1].ls);
trie[rt].mz=max(trie[rt<<1].mz, trie[rt<<1|1].mz); //‘0’节点求长
trie[rt].mz=max(trie[rt].mz, trie[rt<<1].rz+trie[rt<<1|1].lz);
if(trie[rt].ls==trie[rt<<1].r-trie[rt<<1].l+1) //左边是满的话,就可以往右边加了
{
trie[rt].ls+=trie[rt<<1|1].ls;
}
if(trie[rt].rs==trie[rt<<1|1].r-trie[rt<<1|1].l+1) //右边也是满的话
{
trie[rt].rs+=trie[rt<<1].rs;
} //同理可得‘0’节点
if(trie[rt].lz==trie[rt<<1].r-trie[rt<<1].l+1) //左边是满的话,就可以往右边加了
{
trie[rt].lz+=trie[rt<<1|1].lz;
}
if(trie[rt].rz==trie[rt<<1|1].r-trie[rt<<1|1].l+1) //右边也是满的话
{
trie[rt].rz+=trie[rt<<1].rz;
}
}
int Query(int rt, int l, int r, int ql, int qr)
{
if(ql<=l && qr>=r) //其他也可以直接返回的情况
{
return trie[rt].ms;
}
int mid=(l+r)>>1;
pushdown(rt, l, r);
if(qr<=mid) return Query(rt<<1, l, mid, ql, qr);
else if(ql>mid) return Query(rt<<1|1, mid+1, r, ql, qr);
else
{
int aa,bb,cc;
aa=min(mid-ql+1, trie[rt<<1].rs)+min(qr-mid, trie[rt<<1|1].ls);
bb=Query(rt<<1, l, mid, ql, mid);
cc=Query(rt<<1|1, mid+1, r, mid+1, qr);
return max(aa, max(bb, cc)); //遇到mid在ql和qr之间的情况,就要判断是中间长还是左右两个边界连续区间长
}
}
int main()
{
while(scanf("%d",&N)!=EOF)
{
memset(a, 0, sizeof(a));
memset(trie, 0, sizeof(trie));
memset(lazy, 0, sizeof(lazy));
for(int i=1; i<=N; i++)
{
scanf("%d",&a[i]);
}
buildTree(1, 1, N); //建树,接下来就是询问或者修建
scanf("%d",&M);
while(M--)
{
int e1,e2,e3;
scanf("%d%d%d",&e1,&e2,&e3);
if(e1)
{
update(1, 1, N, e2, e3);
}
else
{
printf("%d\n",Query(1, 1, N, e2, e3));
}
}
}
return 0;
}
第二次写的代码比第一次就要高大上的多,毕竟有了第一次的理解,可以作为第二次再做的同学的参考:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
using namespace std;
typedef long long ll;
const int maxN=100005;
struct node
{
int l,r;
int ls,rs,ms; //'1'
int lo,ro,mo; //'0'
}trie[maxN<<2];
int N,M;
int a[maxN];
int lazy[maxN<<2];
void work(int rt)
{
swap(trie[rt].ls, trie[rt].lo);
swap(trie[rt].rs, trie[rt].ro);
swap(trie[rt].ms, trie[rt].mo);
}
void pushup(int rt, int l, int r) //向上更新
{
trie[rt].ls=trie[rt<<1].ls; //'1'
trie[rt].rs=trie[rt<<1|1].rs;
trie[rt].lo=trie[rt<<1].lo; //'0'
trie[rt].ro=trie[rt<<1|1].ro;
trie[rt].ms=max(trie[rt<<1].ms, trie[rt<<1|1].ms); //'1'
trie[rt].mo=max(trie[rt<<1].mo, trie[rt<<1|1].mo); //'0'
trie[rt].ms=max(trie[rt].ms, trie[rt<<1].rs+trie[rt<<1|1].ls); //'1'
trie[rt].mo=max(trie[rt].mo, trie[rt<<1].ro+trie[rt<<1|1].lo); //'0'
if(trie[rt].ls==trie[rt<<1].r-trie[rt<<1].l+1) trie[rt].ls+=trie[rt<<1|1].ls; //'1'
if(trie[rt].rs==trie[rt<<1|1].r-trie[rt<<1|1].l+1) trie[rt].rs+=trie[rt<<1].rs;
if(trie[rt].lo==trie[rt<<1].r-trie[rt<<1].l+1) trie[rt].lo+=trie[rt<<1|1].lo; //'0'
if(trie[rt].ro==trie[rt<<1|1].r-trie[rt<<1|1].l+1) trie[rt].ro+=trie[rt<<1].ro;
}
void pushdown(int rt, int l, int r) //到点更新,即如果lazy标记到了就更新
{
if(lazy[rt])
{
lazy[rt<<1]^=1;
lazy[rt<<1|1]^=1;
work(rt<<1);
work(rt<<1|1);
lazy[rt]=0;
}
}
void buildTree(int rt, int l, int r)
{
if(l==r)
{
trie[rt].l=trie[rt].r=l;
trie[rt].ls=trie[rt].rs=trie[rt].ms=a[l];
trie[rt].lo=trie[rt].ro=trie[rt].mo=1-a[l];
return;
}
trie[rt].l=l;
trie[rt].r=r;
int mid=(l+r)>>1;
buildTree(rt<<1, l, mid);
buildTree(rt<<1|1, mid+1, r);
pushup(rt, l, r);
}
void update(int rt, int l, int r, int ql, int qr)
{
if(ql<=l && qr>=r)
{
lazy[rt]^=1;
work(rt);
return;
}
int mid=(l+r)>>1;
pushdown(rt, l, r);
if(ql<=mid) update(rt<<1, l, mid, ql, qr);
if(qr>=mid+1) update(rt<<1|1, mid+1, r, ql, qr);
pushup(rt, l, r);
}
int Query(int rt, int l, int r, int ql, int qr)
{
if(ql<=l && qr>=r)
{
return trie[rt].ms;
}
int mid=(l+r)>>1;
pushdown(rt, l, r);
if(qr<=mid) return Query(rt<<1, l, mid, ql, qr);
else if(ql>=mid+1) return Query(rt<<1|1, mid+1, r, ql, qr);
else
{
int aa=0,bb=0,cc=0;
aa=min(mid-ql+1, trie[rt<<1].rs)+min(qr-mid, trie[rt<<1|1].ls);
bb=Query(rt<<1, l, mid, ql, mid);
cc=Query(rt<<1|1, mid+1, r, mid+1, qr);
return max(aa, max(bb, cc));
}
}
int main()
{
while(scanf("%d",&N)!=EOF)
{
memset(a, 0, sizeof(a));
memset(lazy, 0, sizeof(lazy));
memset(trie, 0, sizeof(trie));
for(int i=1; i<=N; i++) scanf("%d",&a[i]);
buildTree(1, 1, N);
scanf("%d",&M);
while(M--)
{
int e1,e2,e3;
scanf("%d%d%d",&e1,&e2,&e3);
if(e1) update(1, 1, N, e2, e3);
else printf("%d\n",Query(1, 1, N, e2, e3));
}
}
return 0;
}