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\n", query(1, 1, n, l, r));
        }
    }
    return 0;
}

HDU 3911

  

原文地址:https://www.cnblogs.com/rui-4825/p/12432424.html

时间: 2024-10-11 13:47:46

HDU 3911 Black and White (线段树,区间翻转)的相关文章

HDU 3911 Black And White (线段树区间合并 + lazy标记)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3911 给你n个数0和1,m个操作: 0操作  输出l到r之间最长的连续1的个数 1操作  将l到r之间的0变1,1变0 区间合并的模版题,结构体中的lsum1表示从此区间最左端开始连续1的个数,rsum1表示从此区间最右端开始连续1的个数,sum1表示此区间连续1的个数最长是多少.lsum0,rsum0,sum0也是如此.每一次1的操作将区间内lazy标记与1异或一次,异或两次就说明操作抵消了.然后

hdu 3911 Black And White(线段树)

题目连接:hdu 3911 Black And White 题目大意:给定一个序列,然后有M次操作: 0 l r:表示询问l,r中最大连续1的个数 1 l r:表示将l,r区间上的数取反 解题思路:线段树的一种题型,区间合并,因为有一个取反的操作,所以对于每个节点要维护6个值,包括连续0,1最长序列的长度,左边和右边的最长连续长度.需要注意的是,如果询问的区间最大值是从R[lson] + L[rson]来到,要判断是否比长度大于r - l + 1.一开始没注意,所以WA了,上网搜了下别人的题解,

HDU 3911 Black And White 分段树 题解

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

HDU 1698 Just a Hook (线段树,区间更新)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17214    Accepted Submission(s): 8600 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing f

HDU 1698 Just a Hook(线段树区间替换)

题目地址:HDU 1698 区间替换裸题.同样利用lazy延迟标记数组,这里只是当lazy下放的时候把下面的lazy也全部改成lazy就好了. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #in

hdu 4902 Nice boat(线段树区间修改,输出最终序列)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 Problem Description There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his peopl

hdu 6444 Neko&#39;s loop 线段树区间更新

题目连接:Neko's loop 题意:给一个长度为n的环,下标从0~n-1,环上每个点有个值表示到这个点会得到的快乐值.,然后每次可以花费1能量往后跳k步.你可以选择任意点开始跳,可以任意点结束,最多跳m次问得到至少s的快乐值最初要拥有多少. 题解:先把循环节挑出来,,然后在循环节上找最大字段和.循环节长度为cnt,然后就是枚举起点用线段树维护前缀和,然后取最大值. #include<bits/stdc++.h> #define ls l,m,rt<<1 #define rs m

hdu 1698 Just a Hook 线段树区间更新

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1698 Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks