BZOJ 1251 序列终结者(Splay)

改编自

运用伸展树解决数列维护问题.pdf 

1251: 序列终结者

BZOJ 1251 序列终结者(Splay)

题目大意

 

网上有许多题,就是给定一个序列,要你支持几种操作: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。

 

第一行两个整数 N,M。M 为操作个数。以下 M 行,每行最多四个整数,依次为 K, L, R, V。K 表示是第几种操作,如果不是第 1 种操作则 K 后面只有两个数。对于每个第3种操作,给出正确的回答。

 

【数据范围】N<=50000,M<=100000。

 

做法分析

 

这题还真不能用 线段树 做,Splay 是一个不错的选择

 

对于操作 1 将所有 [L, R] 中的数加上一个常量,可以对 Splay 树中的节点增加一个 add 域,表示以该节点为根中序遍历所得的数列中每个数要增加 add,每次执行的时候,先将 L-1 旋转到根,再把 R+1 旋转成根的儿子节点,那么,[L, R] 区间的数列就在 R+1 的左子树中了,对 R+1 的左儿子执行更新操作即可,类似于线段树的懒操作

对于操作 2 将所有 [L, R] 中的数翻转,可以对 Splay 树中的节点增加一个 rev 域,表示以该节点为根中序遍历所得的数列是否需要翻转,更新的时候还是和操作 1 一样,将 L-1 旋转到根,R+1 旋转成为 L-1 的孩子,直接对 R+1 的左儿子更新

对于操作 3,增加一个 Max 域,表示以该节点为根中序遍历得到的数列中最大值是多少,询问时,将 L-1 旋转到根,R+1 旋转成 L-1 的孩子,询问 R-1 的左儿子的 Max 即可

 

这里说一点细节:由于是维护数列,且涉及到区间翻转操作,所以还要增加一个 Size 域,表示以它为根的中序遍历得到的数列的个数是多少,用它来实现数列中的定位

由于涉及到翻转和统一加权操作,pushDown 和 pushUp 操作一定要想好在哪些地方需要执行

 

参考代码

/*
splay 编号从2..n+2
因为查询[1,n]时, 根为0, 
如果编号为1..n+1, 编号为1-1=0的点要旋转为根(0号节点下面),但是0这个点是用作标记的(而不是根),
    他的size是0,设0为root后size改变,select就不好查找了!
    编号0因为无左儿子,左儿子默认标号是0,而此时0的size已经是n+1了,就会一直向左走 死循环
*/
/*
size[x]: 以x为根节点的树的节点总数
select(pos)是查找中序遍历为pos的节点编号
修改 询问[L..R]时 把中序遍历为L-1的节点变成根(0的右儿子)然后把中序遍历为R+1的放到根的右边(L-1的右儿子) 所需区间就是L-1的左儿子
编号为[2..n+1] 避免麻烦(如果从[1..n+1]会导致size为0的节点不是儿子 增添了许多麻烦(死循环等)
*/ 
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int N=100005, INF=0x7fffffff;

struct Splay_Tree {
    struct Node {
        int val, Max, add, Size, son[2];
        bool rev;
        void init(int _val) {
            val=Max=_val, Size=1;
            add=rev=son[0]=son[1]=0;
        }
    } T[N];
    int fa[N], root;

    void pushUp(int x) {
        T[x].Max=T[x].val, T[x].Size=1;
        if(T[x].son[0]) {
            T[x].Max=max(T[x].Max, T[T[x].son[0]].Max);
            T[x].Size+=T[T[x].son[0]].Size;
        }
        if(T[x].son[1]) {
            T[x].Max=max(T[x].Max, T[T[x].son[1]].Max);
            T[x].Size+=T[T[x].son[1]].Size;
        }
    }

    void pushDown(int x) {
        if(x==0) return;
        if(T[x].add) {
            if(T[x].son[0]) {
                T[T[x].son[0]].val+=T[x].add;
                T[T[x].son[0]].Max+=T[x].add;
                T[T[x].son[0]].add+=T[x].add;
            }
            if(T[x].son[1]) {
                T[T[x].son[1]].val+=T[x].add;
                T[T[x].son[1]].Max+=T[x].add;
                T[T[x].son[1]].add+=T[x].add;
            }
            T[x].add=0;
        }
        if(T[x].rev) {
            if(T[x].son[0]) T[T[x].son[0]].rev^=1;
            if(T[x].son[1]) T[T[x].son[1]].rev^=1;
            swap(T[x].son[0], T[x].son[1]);
            T[x].rev=0;
        }
    }

    void Rotate(int x, int kind) {
        int y=fa[x], z=fa[y];
        T[y].son[!kind]=T[x].son[kind], fa[T[x].son[kind]]=y;
        T[x].son[kind]=y, fa[y]=x;
        T[z].son[T[z].son[1]==y]=x, fa[x]=z;
        pushUp(y);
    }

    void Splay(int x, int goal) {
        if(x==goal) return;
        while(fa[x]!=goal) {
            int y=fa[x], z=fa[y];
            pushDown(z), pushDown(y), pushDown(x);
            int rx=T[y].son[0]==x, ry=T[z].son[0]==y;
            if(z==goal) Rotate(x, rx);
            else {
                if(rx==ry) Rotate(y, ry);
                else Rotate(x, rx);
                Rotate(x, ry);
            }
        }
        pushUp(x);
        if(goal==0) root=x;
    }

    int Select(int pos) {
        int u=root;
        pushDown(u);
        //printf("select %d nowroot = %d siz = %d\n", pos, u, T[T[u].son[0]].Size);

        while(T[T[u].son[0]].Size!=pos) {
            if(pos<T[T[u].son[0]].Size) u=T[u].son[0];
            else {
                pos-=T[T[u].son[0]].Size+1;
                u=T[u].son[1];
            }
            pushDown(u);
            //printf("select %d nowroot = %d siz = %d\n", pos, u, T[T[u].son[0]].Size);

        }
        return u;
    }

    void update(int L, int R, int val) {
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        T[T[v].son[0]].Max+=val;
        T[T[v].son[0]].val+=val;
        T[T[v].son[0]].add+=val;
    }

    void Reverse(int L, int R) {
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        T[T[v].son[0]].rev^=1;
    }

    int query(int L, int R) {
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        return T[T[v].son[0]].Max;
    }

    int build(int L, int R) {
        if(L>R) return 0;
        if(L==R) return L;
        int mid=(L+R)>>1, sL, sR;
        T[mid].son[0]=sL=build(L, mid-1);
        T[mid].son[1]=sR=build(mid+1, R);
        fa[sL]=fa[sR]=mid;
        pushUp(mid);
        return mid;
    }

    void init(int n) {
        T[0].init(-INF), T[1].init(-INF), T[n+2].init(-INF);
        for(int i=2; i<=n+1; i++) T[i].init(0);
        root=build(1, n+2), fa[root]=0;
        fa[0]=0, T[0].son[1]=root, T[0].Size=0;
    }

    void test(int x){
        printf("node %d fa = %d lc = %d rc = %d size = %d\n", 
            x, fa[x], T[x].son[0], T[x].son[1], T[x].Size);
        if (T[x].son[0]) test(T[x].son[0]); 
        if (T[x].son[1]) test(T[x].son[1]);
    }
};

Splay_Tree hehe;

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    hehe.init(n);
    for(int i=0, a, b, c, d; i<m; i++) {
        scanf("%d", &a);
        if(a==1) {
            scanf("%d%d%d", &b, &c, &d);
            hehe.update(b, c, d);
        }
        else if(a==2) {
            scanf("%d%d", &b, &c);
            hehe.Reverse(b, c);
        }
        else {
            scanf("%d%d", &b, &c);
            printf("%d\n", hehe.query(b, c));
        }
    }
    return 0;
}


今天重新研究了一下 之前的做法 基本都是扯淡(上面全部作废)

首先 0-n+1编号的方法在select里面加两句话就ok,就是遍历一个点时判断这个点的儿子是否为0,如果为0就说名没有儿子(而此时T[0].size为0!不特判会走死循环(因为这句while(T[T[u].son[0]].Size!=pos)))

#include <iostream>
#include <cstring>
#include <cstdio>
#define dprintf if (debug) printf
using namespace std;
const int debug = 0;
const int N=100005, INF=0x7fffffff;
  
struct Splay_Tree {
    struct Node {
        int val, Max, add, Size, son[2];
        bool rev;
        void init(int _val) {
            val=Max=_val, Size=1;
            add=rev=son[0]=son[1]=0;
        }
    } T[N];
    int fa[N], root;
  
    void pushUp(int x) {
        T[x].Max=T[x].val, T[x].Size=1;
        if(T[x].son[0]) {
            T[x].Max=max(T[x].Max, T[T[x].son[0]].Max);
            T[x].Size+=T[T[x].son[0]].Size;
        }
        if(T[x].son[1]) {
            T[x].Max=max(T[x].Max, T[T[x].son[1]].Max);
            T[x].Size+=T[T[x].son[1]].Size;
        }
    }
  
    void pushDown(int x) {
        if(x==0) return;
        if(T[x].add) {
            if(T[x].son[0]) {
                T[T[x].son[0]].val+=T[x].add;
                T[T[x].son[0]].Max+=T[x].add;
                T[T[x].son[0]].add+=T[x].add;
            }
            if(T[x].son[1]) {
                T[T[x].son[1]].val+=T[x].add;
                T[T[x].son[1]].Max+=T[x].add;
                T[T[x].son[1]].add+=T[x].add;
            }
            T[x].add=0;
        }
        if(T[x].rev) {
            if(T[x].son[0]) T[T[x].son[0]].rev^=1;
            if(T[x].son[1]) T[T[x].son[1]].rev^=1;
            swap(T[x].son[0], T[x].son[1]);
            T[x].rev=0;
        }
    }
  
    void Rotate(int x, int kind) {
        int y=fa[x], z=fa[y];
        T[y].son[!kind]=T[x].son[kind], fa[T[x].son[kind]]=y;
        T[x].son[kind]=y, fa[y]=x;
        T[z].son[T[z].son[1]==y]=x, fa[x]=z;
        pushUp(y);
    }
  
    void Splay(int x, int goal) {
        if(x==goal) return;
        while(fa[x]!=goal) {
            int y=fa[x], z=fa[y];
            pushDown(z), pushDown(y), pushDown(x);
            int rx=T[y].son[0]==x, ry=T[z].son[0]==y;
            if(z==goal) Rotate(x, rx);
            else {
                if(rx==ry) Rotate(y, ry);
                else Rotate(x, rx);
                Rotate(x, ry);
            }
        }
        pushUp(x);
        if(goal==0) root=x;
    }
  
    int Select(int pos) {
        int u=root;
        pushDown(u);
        dprintf("\nselect %d\n", pos);
        if (pos == 0) return 0;
        while(true) {
            //dprintf("u = %d son0 = %d son1 = %d son0.size = %d\n", u, T[u].son[0], T[u].son[1], T[T[u].son[0]].Size);
            if ((T[u].son[0] && T[T[u].son[0]].Size == pos-1) || (pos == 1 && (!T[u].son[0]))) break;
            if (T[u].son[0] && pos-1<T[T[u].son[0]].Size) u=T[u].son[0];
            else if (T[u].son[1]){
                pos-=T[T[u].son[0]].Size+1;
                u=T[u].son[1];
            }
            pushDown(u);
            //printf("select %d nowroot = %d siz = %d\n", pos, u, T[T[u].son[0]].Size);
  
        }
        dprintf("select return %d\n\n", u);
        return u;
    }
  
    void update(int L, int R, int val) {
        //dprintf("update %d %d %d\n", L, R, val);
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        T[T[v].son[0]].Max+=val;
        T[T[v].son[0]].val+=val;
        T[T[v].son[0]].add+=val;
    }
  
    void Reverse(int L, int R) {
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        T[T[v].son[0]].rev^=1;
    }
  
    int query(int L, int R) {
        int u=Select(L-1), v=Select(R+1);
        Splay(u, 0);
        Splay(v, u);
        return T[T[v].son[0]].Max;
    }
  
    int build(int L, int R) {
        if(L>R) return 0;
        if(L==R) return L;
        int mid=(L+R)>>1, sL, sR;
        T[mid].son[0]=sL=build(L, mid-1);
        T[mid].son[1]=sR=build(mid+1, R);
        fa[sL]=fa[sR]=mid;
        pushUp(mid);
        return mid;
    }
  
    void init(int n) {
        T[0].init(-INF), T[n+1].init(-INF);
        for(int i=1; i<=n; i++) T[i].init(0);
        root=build(1, n+2), fa[root]=0;
        fa[0]=0, T[0].son[1]=root, T[0].Size=0;
    }
  
    void test(int x){
        printf("node %d fa = %d lc = %d rc = %d size = %d\n", 
            x, fa[x], T[x].son[0], T[x].son[1], T[x].Size);
        if (T[x].son[0]) test(T[x].son[0]); 
        if (T[x].son[1]) test(T[x].son[1]);
    }
};
  
Splay_Tree hehe;
  
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    hehe.init(n);
    for(int i=0, a, b, c, d; i<m; i++) {
        scanf("%d", &a);
        if(a==1) {
            scanf("%d%d%d", &b, &c, &d);
            hehe.update(b, c, d);
        }
        else if(a==2) {
            scanf("%d%d", &b, &c);
            hehe.Reverse(b, c);
        }
        else {
            scanf("%d%d", &b, &c);
            printf("%d\n", hehe.query(b, c));
        }
    }
    return 0;
}

然后就可以惊奇的发现:我就是以点号为关键字splay的,我TM要这个select有什么用? 结果发现 反转后会变!!!!!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值