• HDU 3911 Black and White (线段树,区间翻转)


       

    【题目地址】

      vjudge

      HDU

    【题目大意】

    • 海滩上有一堆石头。 石头的颜色是白色或黑色。 小肥羊拥有魔术刷,她可以改变连续石的颜色,从黑变白,从白变黑。 小肥羊非常喜欢黑色,因此她想知道范围[i,j]中连续的黑色石头的最长时间。
    • 有多种情况,每种情况的第一行是整数n(1 <= n <= 10 ^ 5),后跟n个整数1或0(1表示黑石头,0表示白石头),然后是整数 M(1 <= M <= 10 ^ 5)后跟M个运算,格式为xij(x = 0或1),x = 1表示更改范围[i,j]中的石头颜色,并且x = 0表示询问 [i,j]范围内连续黑宝石的最长时间
    • 当x = 0输出时,数字表示范围为[i,j]的黑色宝石的最长长度。

    【样例输入】

    4

    1 0 1 0

    5

    0 1 4

    1 2 3

    0 1 4

    1 3 3

    0 4 4

    【样例输出】

    1

    2

    0

    【一句话题意】

    • 操作指令为0:

      查询【L, R】中最长的连续的1的个数

    • 操作指令为1:

      将区间【L, R】中的0和1翻转

    【难点】

      没办法一步到位求得连续1的个数,如果强买强卖,就是暴力,我们的良心会受到谴责

    【突破】逆推,运用分治的思想化繁为简

      1.我们想要维护区间最长连续1的个数,只需要知道每个子区间内最大前缀1,

       最大后缀1(原因先自己思考下)   

      2.想要维护答案和上述两个后缀长度,并且包含修改操作,那么可以去维护对应的0的个数,

       即区间最长连续0的个数,区间最大前缀0,最大后缀0   

      3.众所周知,区间修改需要lazytag(懒标记)   

      4.(此时没明白不打紧,先往后看)

    【思路梳理】

      一、需要维护的7个变量:

        区间最大前缀1

        区间最大前缀0

        区间最大后缀1

        区间最大后缀0

        区间最大连续1

        区间最大连续0 

        懒惰标记

      二、修改操作

        向下更改时交换所有0,1相关值

      三、查询

        分割区间     

        计算左子区间最大后缀1与当前区间最大前缀1     

        计算右子区间最大前缀1与当前区间最大后缀1     

        将2、3条取max即为所求

      四、注意细节,代码实现

      

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #define int long long
    using namespace std;
    
    inline int read(){
        int x = 0, w = 1;
        char ch = getchar();
        for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
        return x * w;
    }
    
    const int maxn = 1000010;
    int a[maxn];
    
    struct node{
        int l0, r0;
        int l1, r1;
        int max0, max1;
        int lazytag;
    }tree[maxn << 2];
    
    inline void solve(int u, int l, int r){//维护7个相关值
        int mid = (l + r) >> 1;
        //前缀1
        tree[u].l1 = tree[u << 1].l1;
        if(tree[u].l1 == mid - l + 1){
            tree[u].l1 += tree[u << 1 | 1].l1;
        }
        //前缀0
        tree[u].l0 = tree[u << 1].l0;
        if(tree[u].l0 == mid - l + 1){
            tree[u].l0 += tree[u << 1 | 1].l0;
        }
        //后缀1
        tree[u].r1 = tree[u << 1 | 1].r1;
        if(tree[u].r1 == r - mid){
            tree[u].r1 += tree[u << 1].r1;
        }
        //后缀0
        tree[u].r0 = tree[u << 1 | 1].r0;
        if(tree[u].r0 == r - mid){
            tree[u].r0 += tree[u << 1].r0;
        }
        //区间最大1,最大0
        tree[u].max1 = max (max (tree[u << 1].max1, tree[u << 1 | 1].max1), tree[u << 1].r1 + tree[u << 1 | 1].l1);
        tree[u].max0 = max (max (tree[u << 1].max0, tree[u << 1 | 1].max0), tree[u << 1].r0 + tree[u << 1 | 1].l0);
    }
    
    inline void SWAP(int u){//保留备用,每次做区间翻转的时候要用
        swap(tree[u].l0, tree[u].l1);
        swap(tree[u].r0, tree[u].r1);
        swap(tree[u].max0, tree[u].max1);
    }
    
    inline void pushdown(int u, int l, int r){//懒标记下防
        if(l != r){
            tree[u << 1].lazytag ^= 1;
            tree[u << 1 | 1].lazytag ^= 1;
            SWAP(u << 1);
            SWAP(u << 1 | 1);
            tree[u].lazytag = 0;
        }
    }
    
    int tmp;
    inline void build(int u, int l, int r){//建树
        tree[u].lazytag = 0;
        if(l == r){
            tmp = read();
            if(tmp == 1){//若当前石头为黑色
                tree[u].l1 = tree[u].r1 = tree[u].max1 = 1;
                tree[u].l0 = tree[u].r0 = tree[u].max0 = 0;
            }
            else{//为白色
                tree[u].l1 = tree[u].r1 = tree[u].max1 = 0;
                tree[u].l0 = tree[u].r0 = tree[u].max0 = 1;
            }
            return;
        }
        int mid = (l + r) >> 1;
        build(u << 1, l, mid);//左子树
        build(u << 1 | 1, mid + 1, r);//右子树
        solve(u, l, r);//维护7个相关值
    }
    
    inline void change(int u, int l, int r, int s, int t){//区间翻转
        if(l >= s && r <= t){//更新lazytag
            tree[u].lazytag ^= 1;
            SWAP(u);
            return;
        }
        if(tree[u].lazytag) pushdown(u, l, r);
        int mid = (l + r) >> 1;
        if(mid >= t)
            update(u << 1, l, mid, s, t);
        else if(mid < s)
            update(u << 1 | 1, mid + 1, r, s, t);
        else{
            update(u << 1, l, mid, s, t);
            update(u << 1 | 1, mid + 1, r, s, t);
        }
        solve(u, l, r);//维护7个相关值
    }
    
    inline int query(int u, int l, int r, int s, int t){
        if(l >= s && r <= t){
            return tree[u].max1;
        }
        if(tree[u].lazytag) pushdown(u, l ,r);
        int mid = (l + r) >> 1;
        if(mid >= t) return query(u << 1, l, mid, s, t);
        else if(mid < s) return query(u << 1 | 1, mid + 1, r, s, t);
        else{
            int cnt1 = query(u << 1, l, mid, s, t);
            int cnt2 = query(u << 1 | 1, mid + 1, r, s, t);
            int cnt3 = min(mid - s + 1, tree[u << 1].r1);
            int cnt4 = min(t - mid, tree[u << 1 | 1].l1);
            return max(max(cnt1, cnt2), cnt3 + cnt4);
        }
        solve(u, l, r);
    }
    
    signed main(){
        int n;
        while(scanf("%lld", &n) == 1){
            build(1, 1, n);
            int m = read();
            while(m--){
                int opt = read(), l = read(), r = read();
                if(opt == 1)//翻转
                    change(1, 1, n, l, r);
                else//查询
                    printf("%lld
    ", query(1, 1, n, l, r));
            }
        }
        return 0;
    }
    HDU 3911

      

    风吹过,我来过~
  • 相关阅读:
    三、Vue CLI-单页面
    width100%,设置padding或border溢出解决方法
    一、Linux平台部署ASP.NET、ASP.NET CORE、PHP
    二、Core授权-2 之.net core 基于Identity 授权
    一、doT.js使用笔记
    一、域名认证信息
    HB-打包
    一、模型验证CoreWebApi 管道方式(非过滤器处理)2(IApplicationBuilder扩展方法的另一种写法)
    python 写的几道题
    美团面试总结
  • 原文地址:https://www.cnblogs.com/rui-4825/p/12432424.html
Copyright © 2020-2023  润新知