HDU - 3397(线段树区间合并,01覆盖)

链接

题意

有5种操作

  • op = 0 ,改变[a,b]区间值全为0
  • op = 1 ,改变[a,b]区间值全为1
  • op = 2 ,改变[a,b]区间值翻转,0 --> 1 ,1 --> 0
  • op = 3,,查询[a,b]区间所有1的个数
  • op =4,查询[a,b]区间最长连续1的个数

思路

首先很明显需要用线段树来维护区间信息,
线段树维护的信息

int sum[man<<2];//维护区间和
int flag1[man<<2],flag2[man<<2];
//置0,1 flag1 flag1 = 1,置为1, flag1 = 2置为0 ,翻转flag2
int Rlen1[man<<2],Llen1[man<<2],mlen1[man<<2];
//区间右边开始最长连续1,左边开始最长连续1,中间最长连续1
int Rlen0[man<<2],Llen0[man<<2],mlen0[man<<2];
//区间右边开始最长连续0,左边开始最长连续0,中间最长连续0

说明一下为什么需要维护这些信息:sum求区间和实现操作3
Rlen1,Llen1,mlen1实现操作4,Rlen0,Llen0,mlen0,用于操作2翻转,flag1,falg2延迟标记
01翻转就可以用Llen1与Rlen0交换

对于更新

pushup(从下往上更新答案)
inline void pushup(int rt,int l,int r){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    int m = l + r >> 1;
    if(sum[rt<<1] == m - l + 1)Llen1[rt] = Llen1[rt<<1] + Llen1[rt<<1|1];
    //表示左边区间全为1,这个大区间(rt区间)左边连续1就得在加上右区间从左开始连续1的个数
    else Llen1[rt] = Llen1[rt<<1];//否则只为左区间从左开始连续1的个数
    if(sum[rt<<1|1] == r - m)Rlen1[rt] = Rlen1[rt<<1|1] + Rlen1[rt<<1];
    //表示右边区间全为1,这个大区间(rt区间)右边连续1就得在加上左区间从右开始连续1的个数
    else Rlen1[rt] = Rlen1[rt<<1|1];//否则只为左区间从左开始连续1的个数
    mlen1[rt] = Llen1[rt<<1|1] + Rlen1[rt<<1];//中间部分有左区间右连续+右区间左连续
    mlen1[rt] = max(mlen1[rt],max(mlen1[rt<<1],mlen1[rt<<1|1]));//在于左右区间的中间取max

    //下面同理
    if(sum[rt<<1] == 0)Llen0[rt] = Llen0[rt<<1] + Llen0[rt<<1|1];
    else Llen0[rt] = Llen0[rt<<1];
    if(sum[rt<<1|1] == 0)Rlen0[rt] = Rlen0[rt<<1|1] + Rlen0[rt<<1];
    else Rlen0[rt] = Rlen0[rt<<1|1];
    mlen0[rt] = Llen0[rt<<1|1] + Rlen0[rt<<1];
    mlen0[rt] = max(mlen0[rt],max(mlen0[rt<<1],mlen0[rt<<1|1]));
}
pushdown(下推懒惰标记)
inline void pushdown(int rt,int l,int r){
    if(l==r)return;
    int m = l + r >> 1;
    //因为全置操作比xor操作要高,所以要先进行
    if(flag1[rt]){//全置为1 or 0
        flag1[rt<<1] = flag1[rt<<1|1] = flag1[rt];//下推
        flag2[rt<<1|1] = flag2[rt<<1] = 0;//这里必须得把xor的标记给清掉,因为覆盖掉了
        //flag2[rt] = 0不能清掉现在的标记,如果有的话那么一定是在置0,1后面进行的
        //因为我每次全置0,1,我都是把之前的flag2标记置为0了,如果这之后还有,那一定是在后面
        //得把这里置为0之后在翻转。
        int tp = flag1[rt]==1 ? 1 : 0;
        sum[rt<<1] = (m - l + 1)*tp;
        sum[rt<<1|1] = (r - m)*tp;
        Llen1[rt<<1] = Rlen1[rt<<1] = mlen1[rt<<1] = sum[rt<<1];
        Llen1[rt<<1|1] = Rlen1[rt<<1|1] = mlen1[rt<<1|1] = sum[rt<<1|1];
        Llen0[rt<<1] = Rlen0[rt<<1] = mlen0[rt<<1] = m - l + 1 - sum[rt<<1];
        Llen0[rt<<1|1] = Rlen0[rt<<1|1] = mlen0[rt<<1|1] = r - m - sum[rt<<1|1];
        flag1[rt] = 0;
    }
    if(flag2[rt]){//翻转
        flag2[rt<<1] ^= 1;
        flag2[rt<<1|1] ^= 1;
        //0和1翻转,相当于个数交换
        swap(Llen0[rt<<1],Llen1[rt<<1]);
        swap(Rlen0[rt<<1],Rlen1[rt<<1]);
        swap(mlen0[rt<<1],mlen1[rt<<1]);
        swap(Llen0[rt<<1|1],Llen1[rt<<1|1]);
        swap(Rlen0[rt<<1|1],Rlen1[rt<<1|1]);
        swap(mlen0[rt<<1|1],mlen1[rt<<1|1]);
        sum[rt<<1] = m - l + 1 - sum[rt<<1];
        sum[rt<<1|1] = r - m - sum[rt<<1|1];
        flag2[rt] = 0;
    }
}
update更新操作
inline void update(int l,int r,int L,int R,int rt,int op){
   if(L<=l&&r<=R){
       if(1==op||2==op){
           flag1[rt] = op;
           flag2[rt] = 0;//记住这里也得清零
           int tp = op==1 ? 1 : 0;
           sum[rt] = tp*(r-l+1);
           Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt];
           Rlen0[rt] = Llen0[rt] = mlen0[rt] = r - l + 1 - sum[rt];
       }else{
           flag2[rt] ^= 1;//这里是翻转
           //这里其实不用下推全置0,1操作,因为每次都是先下推全置0,1,不会有影响
           sum[rt] = r - l + 1 - sum[rt];
           swap(Llen0[rt],Llen1[rt]);
           swap(Rlen0[rt],Rlen1[rt]);
           swap(mlen0[rt],mlen1[rt]);
       }
       return;
   }
   int m = l + r >> 1;
   pushdown(rt,l,r);
   if(L<=m)update(l,m,L,R,rt<<1,op);
   if(R>m)update(m+1,r,L,R,rt<<1|1,op);
   pushup(rt,l,r);
} 
查询
inline int query1(int l,int r,int L,int R,int rt){
    if(L<=l&&r<=R){
        return sum[rt];
    }
    int m = l + r >>1;
    pushdown(rt,l,r);
    int ans = 0;
    if(L<=m)ans += query1(l,m,L,R,rt<<1);
    if(R>m)ans += query1(m+1,r,L,R,rt<<1|1);
    return ans;
}

inline int query2(int l,int r,int L,int R,int rt){
    if(L<=l&&r<=R){
        return max(max(Llen1[rt],Rlen1[rt]),mlen1[rt]);
    }
    int m = l + r >>1;
    pushdown(rt,l,r);
    int ans = -INT_MAX;
    // if(L<=m)ans = max(ans,query2(l,m,L,R,rt<<1));  //结束条件 L <= l && r <= R
    // if(R>m)ans = max(ans,query2(m+1,r,L,R,rt<<1|1));
    // return max(ans,min(Rlen1[rt<<1],m-L+1)+min(Llen1[rt<<1|1],R-m));
    if(L>m)ans = query2(m+1,r,L,R,rt<<1|1);
    else if(R<=m)ans = query2(l,m,L,R,rt<<1);
    else{
        //ans = max(query2(l,m,L,m,rt<<1),query2(m+1,r,m+1,R,rt<<1|1));// 结束条件 L == l && r == R
        ans = max(query2(l,m,L,R,rt<<1),query2(m+1,r,L,R,rt<<1|1));
        int tp = min(Rlen1[rt<<1],m-L+1) + min(Llen1[rt<<1|1],R-m);
        ans = max(ans,tp);
    }
    return ans;
}

完整代码

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;

typedef long long ll;
const ll mod = 1e9+7;
int a[man];
int sum[man<<2];//维护区间和
int flag1[man<<2],flag2[man<<2];
//置0,1 flag1 flag1 = 1,置为1, flag1 = 2置为0 ,翻转flag2
int Rlen1[man<<2],Llen1[man<<2],mlen1[man<<2];
//区间右边开始最长连续1,左边开始最长连续1,中间最长连续1
int Rlen0[man<<2],Llen0[man<<2],mlen0[man<<2];
//区间右边开始最长连续0,左边开始最长连续0,中间最长连续0

inline void pushup(int rt,int l,int r){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    int m = l + r >> 1;
    if(sum[rt<<1] == m - l + 1)Llen1[rt] = Llen1[rt<<1] + Llen1[rt<<1|1];
    //表示左边区间全为1,这个大区间(rt区间)左边连续1就得在加上右区间从左开始连续1的个数
    else Llen1[rt] = Llen1[rt<<1];//否则只为左区间从左开始连续1的个数
    if(sum[rt<<1|1] == r - m)Rlen1[rt] = Rlen1[rt<<1|1] + Rlen1[rt<<1];
    //表示右边区间全为1,这个大区间(rt区间)右边连续1就得在加上左区间从右开始连续1的个数
    else Rlen1[rt] = Rlen1[rt<<1|1];//否则只为左区间从左开始连续1的个数
    mlen1[rt] = Llen1[rt<<1|1] + Rlen1[rt<<1];//中间部分有左区间右连续+右区间左连续
    mlen1[rt] = max(mlen1[rt],max(mlen1[rt<<1],mlen1[rt<<1|1]));//在于左右区间的中间取max

    //下面同理
    if(sum[rt<<1] == 0)Llen0[rt] = Llen0[rt<<1] + Llen0[rt<<1|1];
    else Llen0[rt] = Llen0[rt<<1];
    if(sum[rt<<1|1] == 0)Rlen0[rt] = Rlen0[rt<<1|1] + Rlen0[rt<<1];
    else Rlen0[rt] = Rlen0[rt<<1|1];
    mlen0[rt] = Llen0[rt<<1|1] + Rlen0[rt<<1];
    mlen0[rt] = max(mlen0[rt],max(mlen0[rt<<1],mlen0[rt<<1|1]));
}

inline void pushdown(int rt,int l,int r){
    if(l==r)return;
    int m = l + r >> 1;
    //因为全置操作比xor操作要高,所以要先进行
    if(flag1[rt]){//全置为1 or 0
        flag1[rt<<1] = flag1[rt<<1|1] = flag1[rt];//下推
        flag2[rt<<1|1] = flag2[rt<<1] = 0;//这里必须得把xor的标记给清掉,因为覆盖掉了
        //flag2[rt] = 0不能清掉现在的标记,如果有的话那么一定是在置0,1后面进行的
        //因为我每次全置0,1,我都是把之前的flag2标记置为0了,如果这之后还有,那一定是在后面
        //得把这里置为0之后在翻转。
        int tp = flag1[rt]==1 ? 1 : 0;
        sum[rt<<1] = (m - l + 1)*tp;
        sum[rt<<1|1] = (r - m)*tp;
        Llen1[rt<<1] = Rlen1[rt<<1] = mlen1[rt<<1] = sum[rt<<1];
        Llen1[rt<<1|1] = Rlen1[rt<<1|1] = mlen1[rt<<1|1] = sum[rt<<1|1];
        Llen0[rt<<1] = Rlen0[rt<<1] = mlen0[rt<<1] = m - l + 1 - sum[rt<<1];
        Llen0[rt<<1|1] = Rlen0[rt<<1|1] = mlen0[rt<<1|1] = r - m - sum[rt<<1|1];
        flag1[rt] = 0;
    }
    if(flag2[rt]){//翻转
        flag2[rt<<1] ^= 1;
        flag2[rt<<1|1] ^= 1;
        //0和1翻转,相当于个数交换
        swap(Llen0[rt<<1],Llen1[rt<<1]);
        swap(Rlen0[rt<<1],Rlen1[rt<<1]);
        swap(mlen0[rt<<1],mlen1[rt<<1]);
        swap(Llen0[rt<<1|1],Llen1[rt<<1|1]);
        swap(Rlen0[rt<<1|1],Rlen1[rt<<1|1]);
        swap(mlen0[rt<<1|1],mlen1[rt<<1|1]);
        sum[rt<<1] = m - l + 1 - sum[rt<<1];
        sum[rt<<1|1] = r - m - sum[rt<<1|1];
        flag2[rt] = 0;
    }
}

inline void build(int l,int r,int rt){
    flag1[rt] = flag2[rt] = 0;
    if(l==r){
        sum[rt] = a[l];
        Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt]; 
        Rlen0[rt] = Llen0[rt] = mlen0[rt] = 1 - sum[rt]; 
        return;
    }
    int m = l + r >> 1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt,l,r);
}

inline void update(int l,int r,int L,int R,int rt,int op){
    if(L<=l&&r<=R){
        if(1==op||2==op){
            flag1[rt] = op;
            flag2[rt] = 0;//记住这里也得清零
            int tp = op==1 ? 1 : 0;
            sum[rt] = tp*(r-l+1);
            Rlen1[rt] = Llen1[rt] = mlen1[rt] = sum[rt];
            Rlen0[rt] = Llen0[rt] = mlen0[rt] = r - l + 1 - sum[rt];
        }else{
            flag2[rt] ^= 1;//这里是翻转
            //这里其实不用下推全置0,1操作,因为每次都是先下推全置0,1,不会有影响
            sum[rt] = r - l + 1 - sum[rt];
            swap(Llen0[rt],Llen1[rt]);
            swap(Rlen0[rt],Rlen1[rt]);
            swap(mlen0[rt],mlen1[rt]);
        }
        return;
    }
    int m = l + r >> 1;
    pushdown(rt,l,r);
    if(L<=m)update(l,m,L,R,rt<<1,op);
    if(R>m)update(m+1,r,L,R,rt<<1|1,op);
    pushup(rt,l,r);
} 

inline int query1(int l,int r,int L,int R,int rt){
    if(L<=l&&r<=R){
        return sum[rt];
    }
    int m = l + r >>1;
    pushdown(rt,l,r);
    int ans = 0;
    if(L<=m)ans += query1(l,m,L,R,rt<<1);
    if(R>m)ans += query1(m+1,r,L,R,rt<<1|1);
    return ans;
}

inline int query2(int l,int r,int L,int R,int rt){
    if(L<=l&&r<=R){
        return max(max(Llen1[rt],Rlen1[rt]),mlen1[rt]);
    }
    int m = l + r >>1;
    pushdown(rt,l,r);
    int ans = -INT_MAX;
    // if(L<=m)ans = max(ans,query2(l,m,L,R,rt<<1));  //结束条件 L <= l && r <= R
    // if(R>m)ans = max(ans,query2(m+1,r,L,R,rt<<1|1));
    // return max(ans,min(Rlen1[rt<<1],m-L+1)+min(Llen1[rt<<1|1],R-m));
    if(L>m)ans = query2(m+1,r,L,R,rt<<1|1);
    else if(R<=m)ans = query2(l,m,L,R,rt<<1);
    else{
        //ans = max(query2(l,m,L,m,rt<<1),query2(m+1,r,m+1,R,rt<<1|1));// 结束条件 L == l && r == R
        ans = max(query2(l,m,L,R,rt<<1),query2(m+1,r,L,R,rt<<1|1));
        int tp = min(Rlen1[rt<<1],m-L+1) + min(Llen1[rt<<1|1],R-m);
        ans = max(ans,tp);
    }
    return ans;
}

inline void print(int l,int r,int rt){
    
   // if(l==r){
        printf("l:%d r:%d ",l,r);
        printf("rt:%d sum:%d llen1:%d rlen1:%d mlen1:%d ",rt,sum[rt],Llen1[rt] , Rlen1[rt] , mlen1[rt]);
        printf("llen0:%d rlen0:%d mlen0:%d ",Llen0[rt] , Rlen0[rt] , mlen0[rt]);
        printf("flag1:%d flag2:%d\n",flag1[rt],flag2[rt]);
       // return;
   // }
   if(l==r)return;
    int m = l + r >> 1;
    print(l,m,rt<<1);
    print(m+1,r,rt<<1|1);
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        //memset(flag1,-1,sizeof(flag1));
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++){
            scanf("%d",&a[i]);
        }
        build(1,n,1);
        //print(1,n,1);
        while(m--){
            int op,a,b;
            scanf("%d%d%d",&op,&a,&b);
            a++,b++;
            if(0==op){
                update(1,n,a,b,1,2);
            }else if(1==op){
                update(1,n,a,b,1,1);
            }else if(2==op){
                update(1,n,a,b,1,3);
            }else if(3==op){
                printf("%d\n",query1(1,n,a,b,1));
            }else{
                printf("%d\n",query2(1,n,a,b,1));
            }
            //print(1,n,1);cout << endl;
        }
    }
    return 0;
}
发布了27 篇原创文章 · 获赞 7 · 访问量 2690

猜你喜欢

转载自blog.csdn.net/weixin_43571920/article/details/103601023
今日推荐