• HDU 3397 Sequence operation 多标记线段树


    /*
    一开始维护了两个标记 开了两个数组
    想的是 可能当前两种操作都要做
    但是太复杂了 不好处理
    其实 当前要做的标记可以只有一个
    
    我们在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
    ",Query1(1,1,n,x,y));
                if(z==4)printf("%d
    ",Query2(1,1,n,x,y));
            }
        }
        return 0;
    }
  • 相关阅读:
    java 分解质因数
    Unix 文件系统读写时权限校验
    find 命令实战巧用
    Linq分组功能
    三种查看SqlServer中数据物理pge页的方法
    一个高度压缩的bit位图字典的实现
    windbg 命令 gchandles
    ManualResetEvent和AutoResetEvent的区别实例
    一些汇编指令(基于8086)
    windbg sos版本不匹配问题解决
  • 原文地址:https://www.cnblogs.com/yanlifneg/p/9447798.html
Copyright © 2020-2023  润新知