POJ 3580 SuperMemo (SPLAY TREE)

本文详细介绍了数据结构如Splay Tree的操作,包括ADD、REVERSE、INSERT、DELETE、MIN等,以及如何通过构建树、反转懒惰标记等方式进行高效处理。通过实例演示了每个操作的具体实现,并提供了代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题的操作非常多

ADD操作和REVERSE操作一样, 把区间变成子树, 然后直接加标记就可以了

REVOLVE 看起来很厉害, 稍微分析一下就可以得出其实就是把区间后面的一部分和放到前面一部分的前面

INSERT 操作可以把插入点之前的结点转到根结点, 插入点之后的结点转到根节点下面, 这样插入的位置就一定是根节点右儿子的左儿子(方便操作, 人为给splay tree加上两个端点结点)

DELETE 操作可以先把删除的结点转成一颗子树, 然后直接删掉就好

MIN 操作也类似, 直接把区间转成子树, 直接出解

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200015;
const int INF = 0x7f7f7f7f;
#define KT ch[ch[root][1]][0]

int root, fa[N], sz[N], ch[N][2];
bool rev[N];
int sum[N], mi[N], num[N];
int tot;

inline void pushup(int x)
{
    int l = ch[x][0], r = ch[x][1];
    sz[x] = 1 + sz[l] + sz[r];
    mi[x] = min(mi[l], mi[r]);
    mi[x] = min(mi[x], num[x]);
}

inline void pushdown(int x)
{
    int &l = ch[x][0], &r = ch[x][1];
    if(rev[x])
    {
        rev[l] ^= 1;
        rev[r] ^= 1;
        rev[x] = false;
        swap(l, r);
    }
    if(sum[x])
    {
        sum[l] += sum[x];
        sum[r] += sum[x];
        if(l) //不加会错, 应该是INF设置的太大, 直接+的话, mi[0]会+爆, 导致以后更新出错
        {
            mi[l] += sum[x];
            num[l] += sum[x];
        }
        if(r)
        {
            mi[r] += sum[x];
            num[r] += sum[x];
        }
        sum[x] = 0;
    }
}

void build(int l, int r, int f)
{
	if(l > r)
		return ;
	int m = (l + r) >> 1; //m是当前结点
	ch[m][0] = (m - 1 >= l)? (m - 1 + l) >> 1: 0;
	ch[m][1] = (r >= m + 1)? (m + 1 + r) >> 1: 0;
	mi[m] = num[m];
	fa[m] = f;
	build(l, m - 1, m);
	build(m + 1, r, m);
	pushup(m);
}

void init()
{
    memset(rev, false, sizeof(rev)); //反转的懒惰标记
    memset(sum, 0, sizeof(sum)); //add的懒惰标记
    sz[0] = 0;
    root = (1 + tot) >> 1;
    build(1, tot, 0);
}

inline void rotate(int x, bool f)
{
    int y = fa[x];
    int z = fa[y];
    pushdown(y);
    pushdown(x);
    ch[y][!f] = ch[x][f];
    fa[ch[x][f]] = y;
    fa[x] = z;
    if(z)
        ch[z][ch[z][1] == y] = x;
    ch[x][f] = y;
    fa[y] = x;
    pushup(y);
}

void splay(int x, int g)
{
    int y = fa[x];
    pushdown(x);
    while(y != g)
    {
        int z = fa[y];
        bool f = ch[y][0] == x;
        if(z != g && f == (ch[z][0] == y))
            rotate(y, f);
        rotate(x, f);
        y = fa[x];
    }
    pushup(x);
    if(g == 0)
        root = x;
}

void rotateto(int k, int g)
{
    int x = root;
    pushdown(x);
    while(sz[ch[x][0]] != k)
    {
        if(k < sz[ch[x][0]])
            x = ch[x][0];
        else
        {
            k -= sz[ch[x][0]] + 1;
            x = ch[x][1];
        }
        pushdown(x);
    }
    splay(x, g);
}

void add(int a, int b, int c)
{
    rotateto(a - 1, 0);
    rotateto(b + 1, root);
    num[KT] += c;
    mi[KT] += c;
    sum[KT] += c;
}

void rever(int a, int b)
{
    rotateto(a - 1, 0);
    rotateto(b + 1, root);
    rev[KT] ^= 1;
}

void revolve(int a, int b, int c)
{
    if(c == 0 || a == b)
        return ;
    int t = c % (b - a + 1);
    if(t == 0)
        return ;
    int _a = b - t + 1 //[_a, b]要放到[a, _a - 1]前面;
    rotateto(_a - 1, 0);
    rotateto(b + 1, root);
    int tmp = KT;
    KT = 0;
    pushup(ch[root][1]);
    pushup(root);
    rotateto(a - 1, 0);
    rotateto(a, root);
    KT = tmp;
    fa[tmp] = ch[root][1];
    pushup(ch[root][1]);
    pushup(root);
}

void inster(int a, int b)
{
    rotateto(a, 0);
    rotateto(a + 1, root);
    num[++tot] = b;
    sz[tot] = 1;
    ch[tot][0] = ch[tot][1] = 0;
    fa[tot] = ch[root][1];
    KT = tot;
    mi[tot] = b;
    pushup(ch[root][1]);
    pushup(root);
}

void delet(int a)
{
    rotateto(a - 1, 0);
    rotateto(a + 1, root);
    KT = 0;
    pushup(ch[root][1]);
    pushup(root);
}

int getmin(int a, int b)
{
    rotateto(a - 1, 0);
    rotateto(b + 1, root);
    return mi[KT];
}

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        mi[0] = INF; //这里要注意
        tot = 0;
        num[1] = INF; //左端点
        tot = n + 2;
        for(int i = 2; i < tot; i++)
            scanf("%d", num + i);
        num[tot] = INF; //右端点
        init(); //建树
        int m;
        scanf("%d", &m);
        while(m--)
        {
            char s[10];
            scanf("%s", s);
            int a, b, c;
            if(s[0] == 'A')
            {
                scanf("%d%d%d", &a, &b, &c);
                add(a, b, c);
            }
            else if(s[0] == 'R')
            {
                scanf("%d%d", &a, &b);
                if(s[3] == 'E')
                    rever(a, b);
                else
                {
                    scanf("%d", &c);
                    revolve(a, b, c);
                }
            }
            else if(s[0] == 'I')
            {
                scanf("%d%d", &a, &b);
                inster(a, b);
            }
            else if(s[0] == 'D')
            {
                scanf("%d", &a);
                delet(a);
            }
            else
            {
                scanf("%d%d", &a, &b);
                printf("%d\n", getmin(a, b));
            }
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值