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

Posted rui-4825

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

 

 

 

  

 

技术图片

 

以上是关于HDU 3911 Black and White (线段树,区间翻转)的主要内容,如果未能解决你的问题,请参考以下文章

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

[HDOJ3911]Black And White(线段树,区间合并)

HDU 5113--Black And White(搜索+剪枝)

HDU 5113 Black And White

搜索(剪枝优化):HDU 5113 Black And White

hdu-5583 Kingdom of Black and White(数学,贪心,暴力)