初识splay

  这东西都没什么板子着实让我很难受啊,只能到网上抄抄补补,

  记下两个用到的博客

https://blog.csdn.net/clove_unique/article/details/50630280

https://blog.csdn.net/ophunter_lcm/article/details/18157185

   BZOJ 3224

  复制粘贴?...

  1 #include <iostream>
  2 #include <string.h>
  3 #include <cstdio>
  4 #include <vector>
  5 #include <queue>
  6 #include <math.h>
  7 #include <string>
  8 #include <algorithm>
  9 #include <time.h>
 10 
 11 #define SIGMA_SIZE 26
 12 #define lson rt<<1
 13 #define rson rt<<1|1
 14 #define lowbit(x) (x&-x)
 15 #define foe(i, a, b) for(int i=a; i<=b; i++)
 16 #define fo(i, a, b) for(int i=a; i<b; i++);
 17 #pragma warning ( disable : 4996 )
 18 
 19 using namespace std;
 20 typedef long long LL;
 21 inline LL LMax(LL a, LL b) { return a>b ? a : b; }
 22 inline LL LMin(LL a, LL b) { return a>b ? b : a; }
 23 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); }
 24 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; }  //a*b = gcd*lcm
 25 inline int Max(int a, int b) { return a>b ? a : b; }
 26 inline int Min(int a, int b) { return a>b ? b : a; }
 27 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); }
 28 inline int lcm(int a, int b) { return a / gcd(a, b)*b; }  //a*b = gcd*lcm
 29 const LL INF = 0x3f3f3f3f3f3f3f3f;
 30 const LL mod = 1000000007;
 31 const double eps = 1e-8;
 32 const int inf = 0x3f3f3f3f;
 33 const int maxk = 1e6 + 5;
 34 const int maxn = 1e5 + 5;
 35 
 36 int Size, root;
 37 int ch[maxn<<2][2];
 38 int f[maxn<<2];
 39 int key[maxn<<2];
 40 int cnt[maxn<<2];
 41 int siz[maxn<<2];
 42 
 43 inline void clear(int x)
 44 { ch[x][0] = ch[x][1] = f[x] = cnt[x] = key[x] = siz[x] = 0; }
 45 
 46 //判断当前点是它父节点的左儿子还是右儿子
 47 inline int get(int x)
 48 { return ch[f[x]][1] == x; }
 49 
 50 //更新当前点size值(用于修改之后)
 51 inline void update(int x)
 52 {
 53     if (x) {
 54         siz[x] = cnt[x];
 55         if (ch[x][0]) siz[x] += siz[ch[x][0]];
 56         if (ch[x][1]) siz[x] += siz[ch[x][1]];
 57     }
 58 }
 59 
 60 inline void rotate(int x)
 61 {
 62     int old = f[x], oldf = f[old], which = get(x);
 63     ch[old][which] = ch[x][which^1]; f[ch[old][which]] = old;
 64     f[old] = x; ch[x][which^1] = old;
 65     f[x] = oldf;
 66     if (oldf)
 67         ch[oldf][ch[oldf][1]==old] = x;
 68     update(old); update(x);
 69 }
 70 
 71 inline void splay(int x)
 72 {
 73     for( int fa; (fa=f[x]); rotate(x))
 74         if ( f[fa] )
 75             rotate((get(x)==get(fa) ? fa : x));
 76     root = x;
 77 }
 78 
 79 inline void insert(int v)
 80 {
 81     if ( root == 0 )
 82     { 
 83         Size++; ch[Size][0] = ch[Size][1] = f[Size] = 0; key[Size] = v;
 84         cnt[Size] = 1; siz[Size] = 1; root = Size; return;
 85     }
 86 
 87     int now = root, fa = 0;
 88     while (1)
 89     {
 90         if (key[now] == v) {
 91             cnt[now]++;
 92             update(now); update(fa);
 93             splay(now);
 94             break;
 95         }
 96         fa = now;
 97         now = ch[now][key[now]<v];
 98         if (now == 0) {
 99             Size++;
100             ch[Size][0] = ch[Size][1] = 0;
101             key[Size] = v; siz[Size] = 1;
102             cnt[Size] = 1; f[Size] = fa;
103             ch[fa][key[fa]<v] = Size;
104             update(fa);
105             splay(Size);
106             break;
107         }
108     }
109 }
110 
111 inline int find(int v)
112 {
113     int ans = 0, now = root;
114     while (1)
115     {
116         if ( v < key[now] )
117             now = ch[now][0];
118         else {
119             ans += (ch[now][0] ? siz[ch[now][0]] : 0);
120             if ( v == key[now] ) { splay(now); return ans+1; }
121 
122             ans += cnt[now];
123             now = ch[now][1];
124         }
125     }
126 }
127 
128 inline int findx(int x)
129 {
130     int now = root;
131     while (1)
132     {
133         if ( ch[now][0] && x <= siz[ch[now][0]] )
134             now = ch[now][0];
135         else {
136             int tmp = ( ch[now][0] ? siz[ch[now][0]] : 0) + cnt[now];
137 
138             if ( x <= tmp )
139                 return key[now];
140             x -= tmp;
141             now = ch[now][1];
142         }
143     }
144 }
145 
146 inline int pre()
147 {
148     int now = ch[root][0];
149     while ( ch[now][1] ) now = ch[now][1];
150     return now;
151 }
152 
153 inline int next()
154 {
155     int now = ch[root][1];
156     while ( ch[now][0] ) now = ch[now][0];
157     return now;
158 }
159 
160 
161 inline void del(int x) {
162     int whatever = find(x);
163     if (cnt[root]>1) { cnt[root]--; return; }
164     //Only One Point
165     if (!ch[root][0] && !ch[root][1]) { clear(root); root = 0; return; }
166     //Only One Child
167     if (!ch[root][0]) {
168         int oldroot = root; root = ch[root][1]; f[root] = 0; clear(oldroot); return;
169     }
170     else if (!ch[root][1]) {
171         int oldroot = root; root = ch[root][0]; f[root] = 0; clear(oldroot); return;
172     }
173     //Two Children
174     int leftbig = pre(), oldroot = root;
175     splay(leftbig);
176     f[ch[oldroot][1]] = root;
177     ch[root][1] = ch[oldroot][1];
178     clear(oldroot);
179     update(root);
180     return;
181 }
182 
183 int main()
184 {
185     int n, opt, x;
186     scanf("%d", &n);
187     for (int i = 1; i <= n; ++i) {
188         scanf("%d%d", &opt, &x);
189         switch (opt) {
190         case 1: insert(x); break;
191         case 2: del(x); break;
192         case 3: printf("%d\n", find(x)); break;
193         case 4: printf("%d\n", findx(x)); break;
194         case 5: insert(x); printf("%d\n", key[pre()]); del(x); break;
195         case 6: insert(x); printf("%d\n", key[next()]); del(x); break;
196         }
197     }
198 
199 
200     return 0;
201 }
View Code

  BZOJ 1251(区间翻转和区间增加一个值V)

  这份代码比较清晰易懂

  https://blog.csdn.net/whai362/article/details/47298133(加了一些注释

/*bzoj 1251 序列终结者
题意:
给定一个长度为N的序列,每个序列的元素是一个整数。要支持以下三种操作:
1. 将[L,R]这个区间内的所有数加上V;
2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1;
3. 求[L,R]这个区间中的最大值;
最开始所有元素都是0。
限制:
N <= 50000, M <= 100000
思路:
伸展树
关键点:
1. 伸展树为左小右大的二叉树,所以旋转操作不会影响树的性质
2. 区间操作为:
int u = select(L - 1), v = select(R + 1);
splay(u, 0); splay(v, u);    //通过旋转操作把询问的区间聚集到根的右子树的左子树下
因为伸展树为左小右大的二叉树,旋转操作后的所以对于闭区间[L, R]之间的所有元素都聚集在根的右子树的左子树下
因为闭区间[L, R],
1) 所以每次都要查开区间(L - 1, R + 1),
2) 所以伸展树元素1对应的标号为2,
3) 所以node[0]对应空节点,node[1]对应比所以元素标号都小的点,node[2 ~ n + 1]对应元素1 ~ n,node[n + 2]对应比所有元素标号都打的点,其中node[0], node[1], node[n + 2]都是虚节点,不代表任何元素。
*/
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <queue>
#include <math.h>
#include <string>
#include <algorithm>
#include <time.h>

#define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) (x&-x)
#define foe(i, a, b) for(int i=a; i<=b; i++)
#define fo(i, a, b) for(int i=a; i<b; i++);
#pragma warning ( disable : 4996 )

using namespace std;
typedef long long LL;
inline LL LMax(LL a, LL b) { return a>b ? a : b; }
inline LL LMin(LL a, LL b) { return a>b ? b : a; }
inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); }
inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; }  //a*b = gcd*lcm
inline int Max(int a, int b) { return a>b ? a : b; }
inline int Min(int a, int b) { return a>b ? b : a; }
inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); }
inline int lcm(int a, int b) { return a / gcd(a, b)*b; }  //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = 1000000007;
const double eps = 1e-8;
const int inf = 0x3f3f3f3f;
const int maxk = 1e6 + 5;
const int maxn = 1e5 + 5;

#define LS(n) node[(n)].ch[0]
#define RS(n) node[(n)].ch[1]

struct Splay {
    struct Node {
        int fa, ch[2];
        bool rev;
        int val, add, maxx, size;
        void init(int _val) {
            val = maxx = _val;
            size = 1;
            add = rev = ch[0] = ch[1] = 0;
        }
    } node[maxn];
    int root;

    ///相当于线段树的pushup和pushdown
    void pushUp(int n) {
        node[n].maxx = Max(node[n].val, Max(node[LS(n)].maxx, node[RS(n)].maxx));
        node[n].size = node[LS(n)].size + node[RS(n)].size + 1;
    }

    void pushDown(int n) {
        if (n == 0) return;
        if (node[n].add) {
            if (LS(n)) {
                node[LS(n)].val += node[n].add;
                node[LS(n)].maxx += node[n].add;
                node[LS(n)].add += node[n].add;
            }
            if (RS(n)) {
                node[RS(n)].val += node[n].add;
                node[RS(n)].maxx += node[n].add;
                node[RS(n)].add += node[n].add;
            }
            node[n].add = 0;
        }
        if (node[n].rev) {
            if (LS(n)) node[LS(n)].rev ^= 1;
            if (RS(n)) node[RS(n)].rev ^= 1;
            swap(LS(n), RS(n));
            node[n].rev = 0;
        }
    }

    ///kind = 0为左旋
    ///kind = 1为右旋
    void rotate(int n, bool kind) {
        int fn = node[n].fa;
        int ffn = node[fn].fa;
        node[fn].ch[!kind] = node[n].ch[kind];
        node[node[n].ch[kind]].fa = fn;

        node[n].ch[kind] = fn;
        node[fn].fa = n;

        node[ffn].ch[RS(ffn) == fn] = n;
        node[n].fa = ffn;
        pushUp(fn);
    }

    void splay(int n, int goal) {
        while (node[n].fa != goal) {
            int fn = node[n].fa;
            int ffn = node[fn].fa;
            //三连pushDown
            pushDown(ffn); pushDown(fn); pushDown(n);
            bool rotate_n = (LS(fn) == n);
            bool rotate_fn = (LS(ffn) == fn);
            if (ffn == goal) rotate(n, rotate_n);
            else {
                if (rotate_n == rotate_fn) rotate(fn, rotate_fn);
                else rotate(n, rotate_n);
                rotate(n, rotate_fn);
            }
        }
        pushUp(n);
        if (goal == 0) root = n;
    }

    ///通过数组中的位置找在树中的位置
    int select(int pos) {
        int u = root;
        pushDown(u);
        while (node[LS(u)].size != pos) {
            if (pos < node[LS(u)].size)
                u = LS(u);
            else {
                pos -= node[LS(u)].size + 1;
                u = RS(u);
            }
            pushDown(u);
        }
        return u;
    }

    int query(int L, int R) {
        int u = select(L - 1), v = select(R + 1);
        splay(u, 0); splay(v, u);    ///通过旋转操作把询问的区间聚集到根的右子树的左子树下
        return node[LS(v)].maxx;
    }

    void pushUpdate(int L, int R, int val) {
        int u = select(L - 1), v = select(R + 1);
        splay(u, 0); splay(v, u);
        node[LS(v)].val += val;
        node[LS(v)].maxx += val;
        node[LS(v)].add += val;
    }

    void reverse(int L, int R) {
        int u = select(L - 1), v = select(R + 1);
        splay(u, 0); splay(v, u);
        node[LS(v)].rev ^= 1;
    }

    ///返回子树的根节点
    int build(int L, int R) {
        if (L > R) return 0;
        if (L == R) return L;
        int mid = (L + R) >> 1;
        int r_L, r_R;
        LS(mid) = r_L = build(L, mid - 1);
        RS(mid) = r_R = build(mid + 1, R);
        node[r_L].fa = node[r_R].fa = mid;
        pushUp(mid);
        return mid;
    }

    ///按照数组的下标顺序作为建树依据
    ///而不是按照数组内的元素大小做依据
    void init(int n) {
        ///0号节点最大值和值都是负无穷
        node[0].init(-inf); node[0].size = 0;
        node[1].init(-inf);
        node[n + 2].init(-inf);
        for (int i = 2; i <= n + 1; ++i)
            node[i].init(0);

        root = build(1, n + 2);
        node[root].fa = 0;

        node[0].fa = 0;
        LS(0) = root;
    }
} splay_tree;


int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    splay_tree.init(n);
    for (int i = 0; i < m; ++i) {
        int op, l, r, v;
        scanf("%d", &op);
        if (op == 1) {
            scanf("%d%d%d", &l, &r, &v);
            splay_tree.pushUpdate(l, r, v);
        }
        else if (op == 2) {
            scanf("%d%d", &l, &r);
            splay_tree.reverse(l, r);
        }
        else {
            scanf("%d%d", &l, &r);
            printf("%d\n", splay_tree.query(l, r));
        }
    }
    return 0;
}
View Code

 

posted @ 2018-07-26 22:46  LBNOQYX  阅读(215)  评论(0编辑  收藏  举报