bzoj 1858 序列操作(线段树)

题外话

本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= =

然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ

Description:

给定一个0/1序列,有如下5个操作:

0:区间赋值为0

1:区间赋值为1

2:区间取反

3:询问区间内1的个数

4:询问区间内最大连续1的个数

Solution

没有操作4这显然就是个SB题,有了操作4我们需要打几个标记

我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数

区间内0/1个数,区间内最大连续1的个数

然后我们就可以合并辣!

具体合并情况想想清楚就好了

Code


#include <bits/stdc++.h>//好题
using namespace std;
#define ls (rt << 1)
#define rs (rt << 1 | 1)
const int N = 100005;
struct Node {
    int lc[2], rc[2], mx[2], cnt[2], len;
}p[N << 2];
int a[N], flag[N << 2];
void pushup(int rt) {
    p[rt].cnt[0] = p[ls].cnt[0] + p[rs].cnt[0];
    p[rt].cnt[1] = p[ls].cnt[1] + p[rs].cnt[1];
    p[rt].lc[0] = p[ls].lc[0] + (p[ls].lc[0] == p[ls].len ? p[rs].lc[0] : 0);
    p[rt].lc[1] = p[ls].lc[1] + (p[ls].lc[1] == p[ls].len ? p[rs].lc[1] : 0);
    p[rt].rc[0] = p[rs].rc[0] + (p[rs].rc[0] == p[rs].len ? p[ls].rc[0] : 0);
    p[rt].rc[1] = p[rs].rc[1] + (p[rs].rc[1] == p[rs].len ? p[ls].rc[1] : 0);
    p[rt].mx[0] = max(p[ls].mx[0], max(p[rs].mx[0], p[ls].rc[0] + p[rs].lc[0]));
    p[rt].mx[1] = max(p[ls].mx[1], max(p[rs].mx[1], p[ls].rc[1] + p[rs].lc[1]));
}
void build(int rt, int l, int r) {
    flag[rt] = -1;
    p[rt].len = r - l + 1;
    if (l == r) {
        p[rt].lc[a[l]] = p[rt].rc[a[l]] = p[rt].mx[a[l]] = p[rt].cnt[a[l]] = 1;
        return;
    }
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    pushup(rt);
}
void pushdown(int rt, int l, int r) {
    if (~flag[rt]) {
        int x = flag[rt];
        if (x == 2) {
            swap(p[ls].lc[0], p[ls].lc[1]), swap(p[rs].lc[0], p[rs].lc[1]);
            swap(p[ls].rc[0], p[ls].rc[1]), swap(p[rs].rc[0], p[rs].rc[1]);
            swap(p[ls].cnt[0], p[ls].cnt[1]), swap(p[rs].cnt[0], p[rs].cnt[1]);
            swap(p[ls].mx[0], p[ls].mx[1]), swap(p[rs].mx[0], p[rs].mx[1]);
            flag[ls] = 1 - flag[ls], flag[rs] = 1 - flag[rs];
        }
        else {
            p[ls].lc[x] = p[ls].rc[x] = p[ls].cnt[x] = p[ls].mx[x] = p[ls].len;
            p[ls].lc[1 - x] = p[ls].rc[1 - x] = p[ls].cnt[1 - x] = p[ls].mx[1 - x] = 0;
            p[rs].lc[x] = p[rs].rc[x] = p[rs].cnt[x] = p[rs].mx[x] = p[rs].len;
            p[rs].lc[1 - x] = p[rs].rc[1 - x] = p[rs].cnt[1 - x] = p[rs].mx[1 - x] = 0;
            flag[ls] = flag[rs] = flag[rt];
        }
        flag[rt] = -1;
    }
}
void change(int rt, int l, int r, int L, int R, int x) {
    if (L <= l && R >= r) {
        p[rt].lc[x] = p[rt].rc[x] = p[rt].cnt[x] = p[rt].mx[x] = p[rt].len;
        p[rt].lc[1 - x] = p[rt].rc[1 - x] = p[rt].cnt[1 - x] = p[rt].mx[1 - x] = 0;
        flag[rt] = x;
        return;
    }
    pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (mid < L)    change(rs, mid + 1, r, L, R, x);
    else if (mid >= R)  change(ls, l, mid, L, R, x);
    else {
        change(ls, l, mid, L, mid, x);
        change(rs, mid + 1, r, mid + 1, R, x);
    }
    pushup(rt);
}
void reverse(int rt, int l, int r, int L, int R) {
    if (L <= l && R >= r) {
        swap(p[rt].lc[0], p[rt].lc[1]), swap(p[rt].rc[0], p[rt].rc[1]);
        swap(p[rt].cnt[0], p[rt].cnt[1]), swap(p[rt].mx[0], p[rt].mx[1]);
        flag[rt] = 1 - flag[rt];
        return;
    }
    pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (mid < L)    reverse(rs, mid + 1, r, L, R);
    else if (mid >= R)  reverse(ls, l, mid, L, R);
    else {
        reverse(ls, l, mid, L, mid);
        reverse(rs, mid + 1, r, mid + 1, R);
    }
    pushup(rt);
}
int ask(int rt, int l, int r, int L, int R) {
    if (L <= l && R >= r)   return p[rt].cnt[1];
    pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (mid < L)    return ask(rs, mid + 1, r, L, R);
    else if (mid >= R)  return ask(ls, l, mid, L, R);
    else return ask(ls, l, mid, L, mid) + ask(rs, mid + 1, r, mid + 1, R);
}
Node ask2(int rt, int l, int r, int L, int R) {
    if (L <= l && R >= r)   return p[rt];
    pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (mid < L)    return ask2(rs, mid + 1, r, L, R);
    else if (mid >= R)  return ask2(ls, l, mid, L, R);
    else {
        Node t1 = ask2(ls, l, mid, L, mid), t2 = ask2(rs, mid + 1, r, mid + 1, R);
        Node t3;
        t3.lc[1] = t1.lc[1] + (t1.lc[1] == t1.len ? t2.lc[1] : 0);
        t3.rc[1] = t2.rc[1] + (t2.rc[1] == t2.len ? t1.rc[1] : 0);
        t3.len = t1.len + t2.len;
        t3.mx[1] = max(t1.mx[1], max(t2.mx[1], t1.rc[1] + t2.lc[1]));
        return t3;
    }
}
int main() {
    int n, q;
    scanf("%d%d",&n, &q);
    for (int i = 1; i <= n; ++i)    scanf("%d", &a[i]);
    build(1, 1, n);
    while (q--) {
        int x, l, r;
        scanf("%d%d%d", &x, &l, &r);
        ++l, ++r;
        switch(x) {
            case 0: change(1, 1, n, l, r, 0);break;
            case 1: change(1, 1, n, l, r, 1);break;
            case 2: reverse(1, 1, n, l, r);break;
            case 3: printf("%d\n", ask(1, 1, n, l, r));break;
            case 4: {
                Node t = ask2(1, 1, n, l, r);
                printf("%d\n", t.mx[1]);
            }
        }
    }
    return 0;
}

时间: 2024-10-26 13:06:40

bzoj 1858 序列操作(线段树)的相关文章

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 2962 序列操作——线段树(卷积?)

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2962 如果 _,_,_,-- 变成了 (_+k),(_+k),(_+k),-- ,计算就是在每个括号里选 _ 或 k ,乘起来求和. 为了算那个,枚举选了 j 个 k :剩下那部分的乘积就是sm[cr][ i-j ]!j 和 k 可以在 len 里除了那 i-j 个位置里选,所以乘上 k^j 再乘上 C( len-(i-j) , j ) . 调了2h+竟然只因组合数推导公式写错-- #in

BZOJ 1858 SCOI2010 序列操作 线段树

题目大意:给定一个01序列,提供三种操作: 0:把一段区间的全部元素都变成0 1:把一段区间的全部元素都变成1 2:把一段区间内的全部元素全都取反 3:查询一段区间内1的个数 4:查询一段区间内最长的一段连续的1 首先假设没有操作4这就是bitset的水题... 多了这个,我们考虑线段树 线段树的每个节点存改动标记和翻转标记,以及该区间的信息 尽管查询的信息都是1 可是我们要连0一起保存 由于翻转后0就变成了1 1就变成了0 区间信息包含: 左端点的元素 右端点的元素 左端点開始的最长的连续元素

【BZOJ-1858】序列操作 线段树

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1961  Solved: 991[Submit][Status][Discuss] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区

bzoj1858 [Scoi2010]序列操作——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1858 线段树...调了一个上午...(后面带 // 的都是改出来的) lazy 标记的下放好麻烦,还得考虑赋值和取反的先后顺序什么的... 因为在取反时把赋值标记 swap 了,所以下放的时候先判断取反再判断赋值... 而且WA了一上午的原因竟然是一开始不慎把取反以为成翻转了,后来没改干净...那个 rev 的名字啊... 总之没有太改变自己最初的想法.改了些细节就A了还是很高兴的! 代码

bzoj1858: [Scoi2010]序列操作 线段树

线段树,维护七个值两个标记. 用结构体快了一倍…… #include<bits/stdc++.h> #define N (1<<18) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=n-1,int k=1 using namespace

Bzoj 2752 高速公路 (期望,线段树)

Bzoj 2752 高速公路 (期望,线段树) 题目链接 这道题显然求边,因为题目是一条链,所以直接采用把边编上号.看成序列即可 \(1\)与\(2\)号点的边连得是. 编号为\(1\)的点.查询的时候把\(r - 1\)就好了. 这里的期望显然就是路径的平均值. 期望值: \[\dfrac{\sum_{i=l}^r\sum_{j=l}^{r}dis[i][j]}{C_{r-l+1}^2}\] 下面部分可以直接算出: 上面这一部分比较难维护. 考虑每一条边会被走过多少次. \[ans = \su

ZYB&#39;s Premutation(有逆序数输出原序列,线段树)

ZYB's Premutation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 758    Accepted Submission(s): 359 Problem Description ZYB has a premutation P,but he only remeber the reverse log of each pr

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl