P3391 【模板】文艺平衡树(Splay)

题目链接

splay的学习链接


 

基于这道题的关于splay的讲解——将由这篇博客开始。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
inline int read()
{
    int x = 0, t = 1; char ch = getchar();
    while( (ch<'0'||ch>'9' ) && ch!='-') ch=getchar();
    if(ch=='-') { t = -1; ch = getchar(); }
    while( ch<='9' && ch>='0' ) { x=x*10+ch-48; ch=getchar(); }
    return x * t;
}
const int maxN = 1e5 + 7;
int root, tot;
int N, M;
struct node
{
    int ff, val, cnt, size, ch[2], lazy;
    node() { ff = val = cnt = size = ch[0] = ch[1] = lazy = 0; }
    void init(int vv, int fa)
    {
        ff = fa;    val = vv;
        cnt = size = 1;
        lazy = ch[0] = ch[1] = 0;
    }
}t[maxN];
inline void pushup(int rt) { t[rt].size = t[t[rt].ch[0]].size + t[t[rt].ch[1]].size + 1; }
inline void pushdown(int rt)
{
    if(t[rt].lazy)
    {
        t[rt].lazy = 0;
        t[t[rt].ch[0]].lazy ^= 1;
        t[t[rt].ch[1]].lazy ^= 1;
        swap(t[rt].ch[0], t[rt].ch[1]);
    }
}
inline void rotate(int x)  //对x进行旋转操作
{
    int y = t[x].ff, z = t[y].ff;   //x的父亲、x的祖父
    int k = t[y].ch[1] == x;    //x是y的哪个父亲
    t[z].ch[t[z].ch[1] == y] = x;   //z原来的y位置变为x
    t[x].ff = z;    //x的父亲变为z
    t[y].ch[k] = t[x].ch[k^1];  //y的原x在y方向上的子节点变为x的反方向的儿子
    t[t[x].ch[k^1]].ff = y; //更新对应点的父节点
    t[x].ch[k^1] = y;   //y是x的k反方向的节点(恒成立)
    t[y].ff = x;    //更新y的父亲
    pushup(y);  pushup(x);  //x和y的旗下节点的个数会变————必须先更新y再更新x
}
inline void splay(int x, int goal)  //将x旋转为goal的儿子,若goal为0,则将x旋转至根的位置
{
    while(t[x].ff != goal)  //一直旋转到x成为goal的儿子
    {
        int y = t[x].ff, z = t[y].ff;   //父节点、祖父节点
        if(z != goal) (t[z].ch[0] == y) ^ (t[y].ch[0] == x) ? rotate(x) : rotate(y);    //若z不是根节点的话,分两种情况讨论
        rotate(x);  //最后都是要旋转x
    }
    if(goal == 0) root = x; //如果goal是0,那个根就是x
}
void find(int x)    //查找值为x的位置并旋转至根节点
{
    int u = root;
    if(!u) return;  //若树为空则直接返回
    while(t[u].ch[x>t[u].val] && x != t[u].val) u = t[u].ch[x>t[u].val];    //若存在儿子并且当前为孩子的值不等于x,跳转至儿子
    splay(u, 0);    //将u旋转至根节点
}
void insert(int x)  //插入x
{
    int u = root, ff = 0;   //当前位置u与父节点ff
    while(u && t[u].val != x)   //当u存在并且还没有移动到当前值
    {
        ff = u; //向下u的儿子
        u = t[u].ch[x>t[u].val];    //大于就向右找,反之向左找
    }
    if(u) t[u].cnt++;   //存在这样的点
    else    //不存在,要新建
    {
        u = ++tot;
        if(ff) t[ff].ch[x>t[ff].val] = u;   //若父节点非根节点、存在父节点的时候
        t[u].init(x, ff);    //建立新节点故左右儿子为空
    }
    splay(u, 0);    //把当前位置移动到根,保证结构平衡
}
int Next(int x, int f)  //0前驱、1后继
{
    find(x);    //找到x的位置并且带到根上去,但是find()找到的点不一定就是恰好等于x的点基本上是"<=x"的点,也有可能是空树
    int u = root;
    if(t[u].val > x && f) return u;     //如果我们找的是后驱并且所找到的根节点就">x"满足后驱的条件就是该点
    if(t[u].val < x && !f) return u;    //如果我们找的是前驱并且满足"<x"
    u = t[u].ch[f];     //在该点的某驱下的方向去取最值
    while(t[u].ch[f^1]) u = t[u].ch[f^1];   //取到尽头
    return u;
}
void Delet(int x)   //删除操作
{
    int last = Next(x, 0), next = Next(x, 1);   //找到前驱和后驱
    splay(last, 0); splay(next, last);      //将前驱放到根节点上去,将后驱链接到前驱上去
    int del = t[next].ch[0];    //那么所对应的要删除的点就是那个既要大于前驱还要小于后驱的点不就是后驱的左儿子
    if(t[del].cnt > 1)
    {
        t[del].cnt--;
        splay(del, 0);
    }
    else t[next].ch[0] = 0;
}
int Kth(int x)
{
    int u = root;
    if(t[u].size < x) return 0;
    while(true)
    {
        pushdown(u);
        int y = t[u].ch[0];
        if(x > t[y].size + t[u].cnt)
        {
            x -= t[y].size + t[u].cnt;
            u = t[u].ch[1];
        }
        else
        {
            if(t[y].size >= x) u = y;
            else return u;
        }
    }
}
inline void work(int l, int r)
{
    l = Kth(l);
    r = Kth(r + 2);
    splay(l, 0);
    splay(r, l);
    t[t[t[root].ch[1]].ch[0]].lazy ^= 1;
}
void write(int u)   //中序遍历——左中右
{
    pushdown(u);
    if(t[u].ch[0]) write(t[u].ch[0]);
    if(t[u].val > 1 && t[u].val <= N + 1) printf("%d ", t[u].val - 1);
    if(t[u].ch[1]) write(t[u].ch[1]);
}
inline void init() { root = tot = 0; }
int main()
{
    scanf("%d%d", &N, &M);
    init();
    for(int i=1; i<=N + 2; i++) insert(i);
    for(int i=1, l, r; i<=M; i++)
    {
//        scanf("%d%d", &l, &r);
        l  = read();    r = read();
        work(l, r);
    }
    write(root);
    printf("\n");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值