BZOJ 1251: 序列终结者 Splay

Description

网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。 【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。
Input

第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。
Output

对于每个第3种操作,给出正确的回答。
Sample Input
4 4

1 1 3 2

1 2 4 -1

2 1 3

3 2 4

Sample Output
2

【数据范围】

N<=50000,M<=100000。

Splay裸题。

//bzoj 1251 splay 复习
//支持操作为
//1: 区间+
//2: 区间翻转
//3: 区间求最大
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50010;
const int maxm = 100010;
namespace SplayTree{
    int siz[maxn], fa[maxn], ma[maxn], rev[maxn], lazy[maxn], val[maxn]; //siz代表节点的sz,fa代表父亲节点,ma代表最大值,rev翻转标记
    //lazy 区间+, val 值
    int ch[maxn][2], tot, root; //tot代表开新节点的时间戳,root代表splay的树根,ch[i][2],i的左右儿子
    void newnode(int &rt, int father, int k){
        rt = ++tot;
        siz[rt] = 1, fa[rt] = father;
        ma[rt] = val[rt] = k;
        rev[rt] = lazy[rt] = ch[rt][0] = ch[rt][1] = 0;
    }
    void pushup(int rt){ //pushup 从底向上更新
        siz[rt] = 1, ma[rt] = val[rt];
        if(ch[rt][0] != 0) siz[rt] += siz[ch[rt][0]], ma[rt] = max(ma[rt], ma[ch[rt][0]]);
        if(ch[rt][1] != 0) siz[rt] += siz[ch[rt][1]], ma[rt] = max(ma[rt], ma[ch[rt][1]]);
    }
    void pushdown(int rt){ //pushdown 从上向下更新
        if(!rt) return;
        if(lazy[rt]){ //区间+懒惰标记传递
            int l = ch[rt][0], r = ch[rt][1];
            if(l != 0) ma[l] += lazy[rt], val[l] += lazy[rt], lazy[l] += lazy[rt];
            if(r != 0) ma[r] += lazy[rt], val[r] += lazy[rt], lazy[r] += lazy[rt];
            lazy[rt] = 0;
        }
        if(rev[rt]){ //区间翻转懒惰标记传递
            int l = ch[rt][0], r = ch[rt][1];
            if(l != 0) rev[l] ^= 1, swap(ch[l][0], ch[l][1]);
            if(r != 0) rev[r] ^= 1, swap(ch[r][0], ch[r][1]);
            rev[rt] ^= 1;
        }
    }
    void rotate(int x){ //旋转,把x转到根节点,kind为1代表右旋,kind为0代表左旋
        int y = fa[x], kind = ch[y][1] == x;
        pushdown(y), pushdown(x);
        ch[y][kind] = ch[x][!kind];
        fa[ch[y][kind]] = y;
        ch[x][!kind] = y;
        fa[x] = fa[y];
        fa[y] = x;
        ch[fa[x]][ch[fa[x]][1] == y] = x;
        pushup(y), pushup(x);
    }
    void splay(int x, int goal) //伸展操作,把根为r的子树调整为goal
    {
        while(fa[x] != goal)
        {
            int y = fa[x], z = fa[y];
            if(z == goal) rotate(x);
            else if((ch[y][1] == x) == (ch[z][1] == y)) rotate(y), rotate(x);
            else rotate(x), rotate(x);
        }
        if(goal == 0) root = x;
    }
    void build(int &rt, int l, int r, int father){ //建立一颗空的splaytree
        if(l > r) return;
        int mid = (l + r) / 2;
        newnode(rt, father, 0); //递归申请新节点
        build(ch[rt][0], l, mid - 1, rt);
        build(ch[rt][1], mid + 1, r, rt);
        pushup(rt);
    }
    int find(int x, int k){ //查找第k位置在splaytree中的位置
        pushdown(x);
        int lsum = siz[ch[x][0]] + 1;
        if(lsum == k) return x;
        else if(lsum < k) return find(ch[x][1], k - lsum);
        else return find(ch[x][0], k);
    }
    void update_val(int l, int r, int v){ //区间更新+
        splay(find(root, l), 0);
        splay(find(root, r + 2), root);
        lazy[ch[ch[root][1]][0]] += v;
        ma[ch[ch[root][1]][0]] += v;
        val[ch[ch[root][1]][0]] += v;
    }
    void update_rev(int l, int r){
        splay(find(root, l), 0);
        splay(find(root, r + 2), root);
        int tmp = ch[ch[root][1]][0]; //现在以tmp为根的splaytree就是l,r区间
        rev[tmp] ^= 1;
        swap(ch[tmp][0], ch[tmp][1]);
    }
    int querymax(int l, int r){ //查询区间最大值
        splay(find(root, l), 0);
        splay(find(root, r + 2), root);
        return ma[ch[ch[root][1]][0]];
    }
}
using namespace SplayTree;
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    newnode(root, 0, -1);
    newnode(ch[root][1], root, -1);
    build(ch[ch[root][1]][0], 1, n, ch[root][1]);
    for(int i = 1; i <= m; i++){
        int cmd, a, b, c;
        scanf("%d", &cmd);
        if(cmd == 1){
            scanf("%d%d%d", &a, &b, &c);
            update_val(a, b, c);
        }else if(cmd == 2){
            scanf("%d%d", &a, &b);
            update_rev(a, b);
        }else{
            scanf("%d%d", &a, &b);
            printf("%d\n", querymax(a, b));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值