luoguP2572 [SCOI2010]序列操作

非常简单的一道线段树题

然而在考场上遇见还是打了$40min$,果然$gedit$不太适合我啊....

维护区间左端,右端,内部最长连续$0/ 1$,以及区间内$0 / 1$的个数即可回答

复杂度$O(n \log n)$

注:讨论线段树标记下放顺序的时候,一定要有组样例,脑想特别容易错!

注2:反转标记和覆盖标记没有绝对明显的顺序,可以根据实现决定谁先下放

#include <cstdio>
#include <iostream>
using namespace std;

#define gc getchar
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;    
}

#define ri register int
#define sid 600050

int n, m;
int w[sid];
struct Seg {
    int lmax[2], rmax[2], max[2], num[2];
    int rev, cov, len;
} t[sid];

#define ls (o << 1)
#define rs (o << 1 | 1)

void pcov(int o, int v) {
    t[o].lmax[v] = t[o].rmax[v] = t[o].max[v] = t[o].num[v] = t[o].len;
    t[o].rev = 0; t[o].cov = v; v ^= 1;
    t[o].lmax[v] = t[o].rmax[v] = t[o].max[v] = t[o].num[v] = 0;
}

void prev(int o) {
    t[o].rev ^= 1;
    swap(t[o].lmax[1], t[o].lmax[0]); swap(t[o].rmax[1], t[o].rmax[0]);
    swap(t[o].max[1], t[o].max[0]); swap(t[o].num[1], t[o].num[0]);
}

void pud(int o) {
    if(t[o].cov != -1) { pcov(ls, t[o].cov); pcov(rs, t[o].cov); t[o].cov = -1; }    
    if(t[o].rev) { prev(ls); prev(rs); t[o].rev = 0; }
}

void upd(int o) {
    for(ri i = 0; i <= 1; i ++) {
        t[o].lmax[i] = t[ls].lmax[i];
        if(t[ls].num[i] == t[ls].len) t[o].lmax[i] = t[rs].lmax[i] + t[ls].len;
        t[o].rmax[i] = t[rs].rmax[i];
        if(t[rs].num[i] == t[rs].len) t[o].rmax[i] = t[ls].rmax[i] + t[rs].len;
        t[o].max[i] = max(t[ls].rmax[i] + t[rs].lmax[i], max(t[rs].max[i], t[ls].max[i]));
        t[o].num[i] = t[ls].num[i] + t[rs].num[i];
    }
}

void Init_Seg(int o, int l, int r) {
    t[o].len = r - l + 1; t[o].cov = -1;
    if(l == r) {
        int v = w[l];
        t[o].lmax[v] = t[o].rmax[v] = 1;
        t[o].max[v] = t[o].num[v] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    Init_Seg(ls, l, mid);
    Init_Seg(rs, mid + 1, r);
    upd(o);
}

void Cov(int o, int l, int r, int ml, int mr, int v) {
    if(ml > r || mr < l) return;
    if(ml <= l && mr >= r) { pcov(o, v); return; }
    pud(o); 
    int mid = (l + r) >> 1;
    Cov(ls, l, mid, ml, mr, v);
    Cov(rs, mid + 1, r, ml, mr, v);
    upd(o);
}

void Rev(int o, int l, int r, int ml, int mr) {
    if(ml > r || mr < l) return;
    if(ml <= l && mr >= r) { prev(o); return; }
    pud(o); 
    int mid = (l + r) >> 1;
    Rev(ls, l, mid, ml, mr);
    Rev(rs, mid + 1, r, ml, mr);
    upd(o); 
}

int Sum(int o, int l, int r, int ml, int mr) {
    if(ml > r || mr < l) return 0;
    if(ml <= l && mr >= r) return t[o].num[1];
    pud(o);
    int mid = (l + r) >> 1; 
    return Sum(ls, l, mid, ml, mr) + Sum(rs, mid + 1, r, ml, mr);
}

int pre, ans;

void Max(int o, int l, int r, int ml, int mr) {
    if(ml > r || mr < l) return;
    if(ml <= l && mr >= r) {
        ans = max(ans, t[o].max[1]);
        ans = max(ans, pre + t[o].lmax[1]);
        if(t[o].num[1] == t[o].len) pre += t[o].len;
        else pre = t[o].rmax[1];
        return;
    }
    pud(o);
    int mid = (l + r) >> 1;
    Max(ls, l, mid, ml, mr);
    Max(rs, mid + 1, r, ml, mr);
}

int main() {
    n = read(); m = read();
    for(ri i = 1; i <= n; i ++) w[i] = read();
    Init_Seg(1, 1, n);
    for(ri i = 1; i <= m; i ++) {
        int opt = read(), l = read() + 1, r = read() + 1;
        if(opt == 0) Cov(1, 1, n, l, r, 0);
        if(opt == 1) Cov(1, 1, n, l, r, 1);
        if(opt == 2) Rev(1, 1, n, l, r);
        if(opt == 3) printf("%d\n", Sum(1, 1, n, l, r));
        if(opt == 4) pre = 0, ans = 0, Max(1, 1, n, l, r), printf("%d\n", ans);
    }
    return 0;
}

 

posted @ 2018-08-25 10:44  remoon  阅读(183)  评论(0编辑  收藏  举报