计蒜客习题: 黑白石头

问题描述

沙滩上有一些石头,石头的颜色是白色或者黑色。小羊会魔法,它能把连续一段的石头,黑色变成白色,白色变成黑色。小羊喜欢黑色,她想知道某些区间中最长的连续黑石头是多少个。
输入格式
第一行一个整数 n(1≤n≤100000)。
第二行 n 个整数,表示石头的颜色,0 是白色,1 是黑色。
第三行一个整数 m(1≤m≤100000)。
接下来 m 行,每行三个整数 x,i,j。
x=1 表示改变 [i,j] 石头的颜色。
x=0 表示询问 [i,j] 最长连续黑色石头的长度。
输出格式
当 x=0 时,输出答案。
样例输入

4
1 0 1 0
5
0 1 4
1 2 3
0 1 4
1 3 3
0 4 4

样例输出

1
2
0

AC代码

#include <cstdio>
#include <algorithm>

using namespace std;
/*务必分清l,r 和tree[p].l,tree[p].r。代码是大佬的。*/
const int kMax = 100000 + 10;
const int kInf = 1e9;

struct node {
    int l, r;
    int lw, rw;
    int lb, rb;
    int maxw, maxb;
    bool change;
} tree[kMax << 2];

void pushup(int p) {//上压统计连续黑色石头的长度
    //左孩子
    tree[p].lw = tree[p << 1].lw;//左孩子里左边白色石头的最长长度
    if(tree[p].lw == tree[p << 1].r - tree[p << 1].l + 1)//左孩子整段都是白色石头
        tree[p].lw += tree[p << 1 | 1].lw;//加上右孩子左边的白色石头

    tree[p].lb = tree[p << 1].lb;//左孩子左边黑色石头长度
    if(tree[p].lb == tree[p << 1].r - tree[p << 1].l + 1)//左孩子全是黑的
        tree[p].lb += tree[p << 1 | 1].lb;//加上右孩子左边里的黑色石头
    //右孩子
    tree[p].rw = tree[p << 1 | 1].rw;//右边白色长度
    if(tree[p].rw == tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1)//全是白色的
        tree[p].rw += tree[p << 1].rw;//加上左孩子右边的白色孩子长度

    tree[p].rb = tree[p << 1 | 1].rb;//右边黑色石头长度
    if(tree[p].rb == tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1)//全是黑的
        tree[p].rb += tree[p << 1].rb;//加左孩子靠右边端的黑色
    //要么左孩子,要么右孩子,要么连起来,取其中最长长度。
    tree[p].maxw = max(max(tree[p << 1].maxw, tree[p << 1 | 1].maxw),
                     tree[p << 1].rw + tree[p << 1 | 1].lw);
    tree[p].maxb = max(max(tree[p << 1].maxb, tree[p << 1 | 1].maxb),
                     tree[p << 1].rb + tree[p << 1 | 1].lb);
}

void pushdown(int p) {
    if(tree[p].change) {
        swap(tree[p << 1].maxw, tree[p << 1].maxb);//反正是最大的
        swap(tree[p << 1].lw, tree[p << 1].lb);//
        swap(tree[p << 1].rw, tree[p << 1].rb);//

        swap(tree[p << 1 | 1].maxw, tree[p << 1 | 1].maxb);//一样
        swap(tree[p << 1 | 1].lw, tree[p << 1 | 1].lb);//
        swap(tree[p << 1 | 1].rw, tree[p << 1 | 1].rb);

        tree[p << 1].change = !tree[p << 1].change;//延迟标记叠加,精髓所在
        tree[p << 1 | 1].change = !tree[p << 1 | 1].change;
        tree[p].change = false;
    }
}

void build(int p, int l, int r) {
    tree[p].l = l;
    tree[p].r = r;
    tree[p].change = false;
    if(l == r) {
        int t;
        scanf("%d", &t);
        tree[p].maxb = tree[p].lb = tree[p].rb = t == 1;//叶子节点是否是黑色
        tree[p].maxw = tree[p].lw = tree[p].rw = t == 0;//叶子节点是否是白色
        return;
    }
    int m = (l + r) >> 1;
    build(p << 1, l, m);
    build(p << 1 | 1, m + 1, r);
    pushup(p);
}

void update(int p, int l, int r) {
    if(l <= tree[p].l && r >= tree[p].r) {
        swap(tree[p].maxw, tree[p].maxb);
        swap(tree[p].lw, tree[p].lb);
        swap(tree[p].rw, tree[p].rb);
        tree[p].change = !tree[p].change;
        return;
    }
    pushdown(p);
    int m = (tree[p].l + tree[p].r) >> 1;
    if(l <= m) update(p << 1, l, r);
    if(r > m) update(p << 1 | 1, l, r);
    pushup(p);
}

int query(int p, int l, int r) {
    if(l <= tree[p].l && r >= tree[p].r) {
        return tree[p].maxb;
    }
    pushdown(p);
    int m = (tree[p].l + tree[p].r) >> 1;
    int res = 0;
    if(l <= m) res = max(res, query(p << 1, l, r));
    if(r > m) res = max(res, query(p << 1 | 1, l, r));
    if(l <= m && r > m) res = max(res,//这里是个坑,l,r可能更小。
                                  min(m - l + 1, tree[p << 1].rb) +
                                  min(r - m, tree[p << 1 | 1].lb));
    pushup(p);
    return res;
}

int main() {
    int n, m, op, l, r;
    scanf("%d", &n);
    build(1, 1, n);
    scanf("%d", &m);
    while(m --) {
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1) {
            update(1, l, r);
        } else {
            printf("%d\n", query(1, l, r));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/liukairui/article/details/80906399
今日推荐