标签:线段树
1 10 10 0 0 0 1 1 0 1 0 1 1 1 0 2 3 0 5 2 2 2 4 0 4 0 3 6 2 3 7 4 2 8 1 0 5 0 5 6 3 3 9
5 2 65
这道题是前面线段树知识的综合题,也和之前有些不同,是个很好的训练题,做了很长时间啊,还需努力。这里的难点是异或运算,即把区间中的0变成1,1变成0.这里我维护每个区间的llen0,rlen0,tlen0,llen1,rlen1,tlen1,即区间从左端点起的向右最大连续0(1)区间长度,从右端起的最大向左连续0(1)区间长度,以及区间最大的连续0(1)区间长度,然后异或操作的时候,只要把0,1换一下,然后用pushup操作就可以了,再记录一个异或操作符x_or.
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define maxn 100005 int a[maxn]; struct node{ int l,r,sum,color,x_or; int llen0,rlen0,tlen0; int llen1,rlen1,tlen1; }b[4*maxn]; int max(int a,int b){ return a>b?a:b; } int min(int a,int b){ return a<b?a:b; } void XOR(int i) { int mid; swap(b[i].llen0,b[i].llen1); swap(b[i].rlen0,b[i].rlen1); swap(b[i].tlen0,b[i].tlen1); b[i].sum=b[i].r-b[i].l+1-b[i].sum; } void pushup(int i) { if(b[i].l==b[i].r)return; b[i].tlen0=max(b[i*2].tlen0,b[i*2+1].tlen0); b[i].tlen0=max(b[i].tlen0,b[i*2].rlen0+b[i*2+1].llen0); b[i].llen0=b[i*2].llen0;b[i].rlen0=b[i*2+1].rlen0; if(b[i*2].llen0==b[i*2].r-b[i*2].l+1)b[i].llen0+=b[i*2+1].llen0; if(b[i*2+1].rlen0==b[i*2+1].r-b[i*2+1].l+1)b[i].rlen0+=b[i*2].rlen0; b[i].tlen1=max(b[i*2].tlen1,b[i*2+1].tlen1); b[i].tlen1=max(b[i].tlen1,b[i*2].rlen1+b[i*2+1].llen1); b[i].llen1=b[i*2].llen1;b[i].rlen1=b[i*2+1].rlen1; if(b[i*2].llen1==b[i*2].r-b[i*2].l+1)b[i].llen1+=b[i*2+1].llen1; if(b[i*2+1].rlen1==b[i*2+1].r-b[i*2+1].l+1)b[i].rlen1+=b[i*2].rlen1; b[i].sum=b[i*2].sum+b[i*2+1].sum; } void build(int l,int r,int i) { int mid; b[i].l=l;b[i].r=r;b[i].color=-1;b[i].x_or=0; if(l==r){ b[i].llen0=b[i].rlen0=b[i].tlen0=a[l]?0:1; b[i].llen1=b[i].rlen1=b[i].tlen1=a[l]?1:0; b[i].color=a[l]; b[i].sum=a[l];return; } mid=(l+r)/2; build(l,mid,i*2); build(mid+1,r,i*2+1); pushup(i); } void pushdown(int i) { int mid; if(b[i].l==b[i].r)return; if(b[i].color!=-1){ //如果是纯色,那么需要下传 b[i*2].color=b[i*2+1].color=b[i].color; b[i*2].x_or=b[i*2+1].x_or=0; //这里因为左右子树的值和父亲一样,所以如果父亲是纯色且翻转一次,那么传到左右子树也是一样的,不管子树记录多少次(先记为0,再后面一步变成1) b[i*2].llen0=b[i*2].rlen0=b[i*2].tlen0=b[i].color?0:(b[i*2].r-b[i*2].l+1); b[i*2].llen1=b[i*2].rlen1=b[i*2].tlen1=b[i].color?(b[i*2].r-b[i*2].l+1):0; b[i*2].sum=b[i].color?(b[i*2].r-b[i*2].l+1):0; b[i*2+1].llen0=b[i*2+1].rlen0=b[i*2+1].tlen0=b[i].color?0:(b[i*2+1].r-b[i*2+1].l+1); b[i*2+1].llen1=b[i*2+1].rlen1=b[i*2+1].tlen1=b[i].color?(b[i*2+1].r-b[i*2+1].l+1):0; b[i*2+1].sum=b[i].color?(b[i*2+1].r-b[i*2+1].l+1):0; b[i].color=-1; } if(b[i].x_or){ b[i*2].x_or^=1; b[i*2+1].x_or^=1; b[i].x_or=0; XOR(i*2); XOR(i*2+1); } } void update(int l,int r,int flag,int i) { int mid; //if(b[i].color==flag)return; 这里和之前做过的染色题不同,颜色相同不能直接返回,因为可能这段上有翻转标记,比如整段区间都是0,但是x_or=1,那么其实这段是1,当flag=0时是要更新的,如果返回就错了。 pushdown(i); //这里也和之前的题目不同,之前都是在第一个if下面写的,这里提前写是因为要把翻转信息提前传给子树,如果没有的话,可能下面第一个if中翻转标记就消失了,那么子树就没有得到翻转信息。 if(b[i].l==l && b[i].r==r){ if(flag<2){ b[i].color=flag; b[i].x_or=0; b[i].llen0=b[i].rlen0=b[i].tlen0=flag?0:(b[i].r-b[i].l+1); b[i].llen1=b[i].rlen1=b[i].tlen1=flag?(b[i].r-b[i].l+1):0; b[i].sum=flag?(b[i].r-b[i].l+1):0; } else{ b[i].x_or=1; XOR(i); } return; } mid=(b[i].l+b[i].r)/2; if(r<=mid)update(l,r,flag,i*2); else if(l>mid)update(l,r,flag,i*2+1); else { update(l,mid,flag,i*2); update(mid+1,r,flag,i*2+1); } pushup(i); } int question(int l,int r,int flag,int i) { int mid,r1,l1,ans1,ans2; if(b[i].l==l && b[i].r==r){ if(flag==3)return b[i].sum; else return b[i].tlen1; } pushdown(i); //询问操作可以在if后面的 mid=(b[i].l+b[i].r)/2; if(r<=mid) return question(l,r,flag,i*2); else if(l>mid)return question(l,r,flag,i*2+1); if(flag==3)return question(l,mid,flag,i*2)+question(mid+1,r,flag,i*2+1); if(b[i*2+1].l+b[i*2+1].llen1-1>r)r1=r; else r1=b[i*2+1].l+b[i*2+1].llen1-1; if(b[i*2].r-b[i*2].rlen1+1<l)l1=l; else l1=b[i*2].r-b[i*2].rlen1+1; ans1=r1-l1+1; ans2=max(question(l,mid,flag,i*2),question(mid+1,r,flag,i*2+1)); return max(ans1,ans2); } int main() { int n,m,i,j,T,flag,c,d; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&a[i]); } build(1,n,1); for(i=1;i<=m;i++){ scanf("%d%d%d",&flag,&c,&d); c++;d++; if(flag<=2){ update(c,d,flag,1); } else printf("%d\n",question(c,d,flag,1)); } } return 0; }
标签:线段树
原文地址:http://blog.csdn.net/kirito_acmer/article/details/46274571