码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 3397 Sequence operation 多标记线段树

时间:2018-08-09 12:09:20      阅读:123      评论:0      收藏:0      [点我收藏+]

标签:mes   query   线段树   翻转   cstring   一个   ==   ace   div   

/*
一开始维护了两个标记 开了两个数组
想的是 可能当前两种操作都要做
但是太复杂了 不好处理
其实 当前要做的标记可以只有一个

我们在Insert的时候 

要打的标记是2即翻转区间: 

1.如果原来是区间赋值1 先赋值1在翻转 问题不大 标记变 1-1=0 
2.如果原来是区间赋值0 同上 问题不大 标记 1-0=1
3.如果原始是区间翻转 抵消1-2=-1
4.如果原来是-1 无标记 就打上 1-(-1)=2 这个时候要先把区间的01信息互换 

要打的标记是01即区间赋值 
就无视之前的翻转操作 直接打上标记更新k 

然后Up的时候(标记下方)就只有一个标记 
1.如果是2 就下放 改了左右儿子 然后同上进行标记改变 
2.如果是01 
*/
#include<cstdio>
#include<cstring>
#include<iostream> 
#define lc k*2
#define rc k*2+1
#define len (r-l+1)
#define mid (l+r)/2
#define maxn 400010
using namespace std;
int T,I,n,m,s1[maxn],s0[maxn],l1[maxn],l0[maxn],r1[maxn],r0[maxn],mx1[maxn],mx0[maxn],a[maxn];
void Swap(int k,int l,int r){
    a[k]=1-a[k];swap(s1[k],s0[k]);swap(l1[k],l0[k]);
    swap(r1[k],r0[k]);swap(mx1[k],mx0[k]);
}
void Up(int k,int l,int r){
    if(a[k]==2){
        a[k]=-1;Swap(lc,l,mid);Swap(rc,mid+1,r);return;
    }
    a[lc]=a[rc]=a[k];
    s1[lc]=(mid-l+1)*a[k];s0[lc]=(mid-l+1)*(a[k]^1);
    s1[rc]=(r-mid)*a[k];s0[rc]=(r-mid)*(a[k]^1);
    l1[lc]=(mid-l+1)*a[k];l0[lc]=(mid-l+1)*(a[k]^1);
    l1[rc]=(r-mid)*a[k];l0[rc]=(r-mid)*(a[k]^1);
    r1[lc]=(mid-l+1)*a[k];r0[lc]=(mid-l+1)*(a[k]^1);
    r1[rc]=(r-mid)*a[k];r0[rc]=(r-mid)*(a[k]^1);
    mx1[lc]=(mid-l+1)*a[k];mx0[lc]=(mid-l+1)*(a[k]^1);
    mx1[rc]=(r-mid)*a[k];mx0[rc]=(r-mid)*(a[k]^1);
    a[k]=-1;
}
void Insert(int k,int l,int r,int x,int y,int z){
    if(x<=l&&y>=r){
        if(z==2)Swap(k,l,r);
        else {
            a[k]=z;s1[k]=len*z;s0[k]=len*(z^1);mx1[k]=len*z;mx0[k]=len*(z^1);
            l1[k]=len*z;l0[k]=len*(z^1);r1[k]=len*z;r0[k]=len*(z^1);
        }
        return;
    }
    if(a[k]!=-1)Up(k,l,r);
    if(x<=mid)Insert(lc,l,mid,x,y,z);
    if(y>mid)Insert(rc,mid+1,r,x,y,z);
    s1[k]=s1[lc]+s1[rc];s0[k]=s0[lc]+s0[rc];
    l1[k]=l1[lc]+(l1[lc]==mid-l+1)*l1[rc];
    r1[k]=r1[rc]+(r1[rc]==r-mid)*r1[lc];
    l0[k]=l0[lc]+(l0[lc]==mid-l+1)*l0[rc];
    r0[k]=r0[rc]+(r0[rc]==r-mid)*r0[lc];
    mx1[k]=max(mx1[lc],max(mx1[rc],r1[lc]+l1[rc]));
    mx0[k]=max(mx0[lc],max(mx0[rc],r0[lc]+l0[rc]));
}
int Query1(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r)return s1[k];
    int res=0;
    if(a[k]!=-1)Up(k,l,r);
    if(x<=mid)res+=Query1(lc,l,mid,x,y);
    if(y>mid)res+=Query1(rc,mid+1,r,x,y);
    return res;
}
int Query2(int k,int l,int r,int x,int y){
    if(x<=l&&y>=r)return mx1[k];
    if(a[k]!=-1)Up(k,l,r);
    int res=0;
    if(x<=mid)res=max(res,Query2(lc,l,mid,x,y));
    if(y>mid)res=max(res,Query2(rc,mid+1,r,x,y));
    if(x<=mid&&y>mid)res=max(res,min(mid-x+1,r1[lc])+min(y-mid,l1[rc]));
    return res;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);int x,y,z;
        for(int i=1;i<=n*4;i++)a[i]=-1;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);Insert(1,1,n,i,i,x);
        }
        for(I=1;I<=m;I++){
            scanf("%d%d%d",&z,&x,&y);x++;y++;
            if(z==0||z==1||z==2)Insert(1,1,n,x,y,z);
            if(z==3)printf("%d\n",Query1(1,1,n,x,y));
            if(z==4)printf("%d\n",Query2(1,1,n,x,y));
        }
    }
    return 0;
}

 

HDU 3397 Sequence operation 多标记线段树

标签:mes   query   线段树   翻转   cstring   一个   ==   ace   div   

原文地址:https://www.cnblogs.com/yanlifneg/p/9447798.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!