SuperMemo 【POJ - 3580】【Splay+懒标记递推想法】

2023-11-09

题目链接


  可以说这道题很好的给我们讲述了在Splay树上的lazy标记的递推,跟线段树上类似,在这棵二叉搜索树上,我们一样的去递推懒标记,接下来说说在哪几处需要专门注意懒标记的使用。

  这里有几处需要注意的地方,就是一开始给你的元素不是已经排好序的,不是固定顺序的(这题似乎本身就是诡异,什么都不讲明白,连数据范围都没说明白)但是仍然是一道好题的说,至少是第一次这么充分的接触到了lazy标记。

  首先就是第一项的操作,给予(l, r)区间都加上值T,这就是将(l-1)这个点提出来,再提出(r+1)然后链接起来,在后者的左子树上推add标记即可。

  第二项的操作是反转操作,跟上一个相似的,我们依旧这么处理,在根据二叉树的性质,只需要从一个头节点往下去不断的交换左右的位置关系,就可以达到翻转的作用了。

  第三项操作是把(l, r)后面T个元素不断的放到头上去的这样一个操作,简单的可以看成是两个区间相互交换位置,(l, r-T)与(r-T+1, r)来交换位置,所以,不妨将后面的那个取出来,然后再插到前面那个前面即可。

  第四项操作是在第X节点后面插入P权值节点,直接用上之前的思维即可。

  第五项操作是删除操作,删除第K个节点,也同上的想法。

  第六项的操作就是求【l, r】的区间最小值,那么就是将这段区间提出来,然后输出其最上面的头的求得的最小值即可,当然,这样子要求的基础就是lazy标记的下移还有回来的更新操作相互作用,才能达到这样的直接输出。

  好了,上面的操作我们讲完了,接下来讲一下懒标记。

  我们需要在哪些地方向下推懒标记呢?在进行旋转(不是题目中的个操作旋转,是Splay本身所具备的旋转操作)的时候,我们会发现会改变原有的splay树的形状,所以,在将x节点和y节点以及z节点改变位置的时候,我们首先要做的就是pusndown()的操作,然后当每次的x与y换好位置的时候,需要逐步更新y和x,就是需要向上pushup()了;在Splay()的时候树的形状也会改变,所以,我们在将x挪到goal的下面的时候,需要同时去pushdown()祖父z、父亲y、自己本身x这三个节点,然后就可以了;再之后,可以看到,求区间第K个的时候,需要向下去寻找,此时,因为有翻转操作,所以我们应该先pushdown()再去查询,所以在这里也要向下推标记。

  还有一个特别的地方就是一开始按序输入的N个数的地方,需要有一些小的处理,可以变得方便的多,我们可以每次插入一个数,并且将其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;
const int maxN = 1e5 + 7;
int N, M, root, tot;
struct node
{
    int ff, val, ch[2], size, cnt, lazy, add, minn;
    node() { ff = val = ch[0] = ch[1] = size = cnt = lazy = add = 0; minn = INF; }
    void init(int fa, int _val) { ff = fa;  val = minn = _val; ch[0] = ch[1] = lazy = add = 0; size = cnt = 1; }
}t[maxN<<1];
void pushup(int rt)
{
    if(!rt) return;
    t[rt].size = t[t[rt].ch[0]].size + t[t[rt].ch[1]].size + t[rt].cnt;
    t[rt].minn = t[rt].val;
    if(t[rt].ch[0]) t[rt].minn = min(t[rt].minn, t[t[rt].ch[0]].minn);
    if(t[rt].ch[1]) t[rt].minn = min(t[rt].minn, t[t[rt].ch[1]].minn);
}
void pushdown(int rt)
{
    if(!rt) return;
    if(t[rt].add)
    {
        if(t[rt].ch[0])
        {
            t[t[rt].ch[0]].add += t[rt].add;
            t[t[rt].ch[0]].val += t[rt].add;
            t[t[rt].ch[0]].minn += t[rt].add;
        }
        if(t[rt].ch[1])
        {
            t[t[rt].ch[1]].add += t[rt].add;
            t[t[rt].ch[1]].val += t[rt].add;
            t[t[rt].ch[1]].minn += t[rt].add;
        }
        t[rt].add = 0;
    }
    if(t[rt].lazy)
    {
        t[rt].lazy = 0;
        if(t[rt].ch[0]) t[t[rt].ch[0]].lazy ^= 1;
        if(t[rt].ch[1]) t[t[rt].ch[1]].lazy ^= 1;
        swap(t[rt].ch[0], t[rt].ch[1]);
    }
}
void Rotate(int x)
{
    int y = t[x].ff, z = t[y].ff;
    pushdown(y);    pushdown(x);
    int k = t[y].ch[1] == x;
    t[z].ch[t[z].ch[1] == y] = x;
    t[x].ff = z;
    t[y].ch[k] = t[x].ch[k^1];
    t[t[x].ch[k^1]].ff = y;
    t[x].ch[k^1] = y;
    t[y].ff = x;
    pushup(y);  pushup(x);
}
void Splay(int x, int goal)
{
    pushdown(x);
    while(t[x].ff != goal)
    {
        int y = t[x].ff, z = t[y].ff;
        pushdown(z);    pushdown(y);    pushdown(x);    //这里就要下传标记了,避免树形改变导致的旋转出错
        if(z != goal) (t[z].ch[0] == y) ^ (t[y].ch[0] == x) ? Rotate(x) : Rotate(y);
        Rotate(x);
    }
    if(!goal) root = x;
}
void insert(int x)
{
    int u = ++tot;
    t[root].ch[1] = u;
    t[u].init(root, x);
    Splay(u, 0);
    if(!root) root = u;
}
int Kth(int x)
{
    int u = root;
    while(true)
    {
        pushdown(u);
        int y = t[u].ch[0];
        if(t[y].size + t[u].cnt < x)
        {
            x -= t[y].size + t[u].cnt;
            u = t[u].ch[1];
        }
        else
        {
            if(t[y].size >= x) u = y;
            else return u;
        }
    }
}
void ADD(int l, int r, int val)
{
    int x = Kth(l - 1), y = Kth(r + 1);
    Splay(x, 0);    Splay(y, x);
    int tmp = t[y].ch[0];
    if(tmp)
    {
        t[tmp].val += val;
        t[tmp].add += val;
        t[tmp].minn += val;
    }
}
void REVOLVE(int l1, int r1, int l2, int r2)    //将(l1, r1)与区间(l2, r2)交换位置
{
    int x = Kth(l2 - 1), y = Kth(r2 + 1);
    Splay(x, 0);    Splay(y, x);
    int tmp = t[y].ch[0];
    t[y].ch[0] = 0;
    x = Kth(l1 - 1);    y = Kth(l1);
    Splay(x, 0);    Splay(y, x);
    t[y].ch[0] = tmp;
    t[tmp].ff = y;
}
inline void init()
{
    root = tot = 0;
    memset(t, 0, sizeof(t));
    insert(INF);
}
char op[10];
int main()
{
    while(scanf("%d", &N) != EOF)
    {
        init();
        for(int i=1, val; i<=N; i++)
        {
            scanf("%d", &val);
            insert(val);
        }
        insert(INF);
        scanf("%d", &M);
        int x, y, T;
        while(M--)
        {
            scanf("%s", op);
            scanf("%d", &x);
            if(op[0] == 'A')        //ADD
            {
                scanf("%d", &y);
                scanf("%d", &T);
                ADD(x+1, y+1, T);
            }
            else if(op[0] == 'I')   //INSERT:第x数后面插入值为y的数
            {
                scanf("%d", &y);
                int l = Kth(x + 1), r = Kth(x + 2);
                Splay(l, 0);    Splay(r, l);
                int tmp = ++tot;
                t[r].ch[0] = tmp;
                t[tmp].val = y;
                t[tmp].size = 1;
                t[tmp].cnt = 1;
                t[tmp].ff = r;
                t[tmp].lazy = t[tmp].add = 0;
                t[tmp].ch[0] = t[tmp].ch[1] = 0;
                Splay(tmp, 0);
            }
            else if(op[0] == 'D')   //DELETE
            {
                int l = Kth(x), r = Kth(x + 2);
                Splay(l, 0);    Splay(r, l);
                t[r].ch[0] = 0;
                pushup(t[root].ch[1]);
                pushup(root);
            }
            else if(op[0] == 'M')   //MIN
            {
                scanf("%d", &y);
                int l = Kth(x), r = Kth(y + 2);
                Splay(l, 0);    Splay(r, l);
                int tmp = t[r].ch[0];
                printf("%d\n", t[tmp].minn);
            }
            else if(op[3] == 'E')   //REVERSE
            {
                scanf("%d", &y);
                int l = Kth(x), r = Kth(y + 2);
                Splay(l, 0);    Splay(r, l);
                int tmp = t[r].ch[0];
                t[tmp].lazy ^= 1;
            }
            else        //REVOLVE
            {
                scanf("%d%d", &y, &T);
                T = T%(y - x + 1);
                if(T) REVOLVE(x + 1, y - T + 1, y - T + 2, y + 1);
            }
        }
    }
    return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
15
ADD 4 8 3
MIN 5 7
MIN 7 10
REVERSE 2 5
MIN 2 6
MIN 2 3
INSERT 3 4
MIN 3 4
MIN 5 10
DELETE 6
MIN 3 5
MIN 4 4
REVOLVE 3 6 7
MIN 5 8
MIN 7 10
ans:
8
9
2
7
4
2
3
4
7
9
*/

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SuperMemo 【POJ - 3580】【Splay+懒标记递推想法】 的相关文章

  • TCP协议 ---可靠传输的各种机制

    目录 一 可靠 确认应答机制 保证数据可靠 有序的到达对端 超时重传机制 二 效率 2 1 提高自身发送数据量 滑动窗口机制 滑动窗口的发送缓冲区是环形队列 滑动窗口的大小会变化吗 滑动窗口的nb之处 滑动窗口丢包问题 2 2 对方的接受能
  • twrp3.3.0刷n9002_【笔刷】iPad Procreate5.0新笔刷分享,巨好用!

    01 Procreate 趣味复古蜡笔纹理笔刷15款 适用软件 Procreate5 0以上 适用系统 ipad系统 笔刷格式 brushset 素材大小 93MB 笔刷视频演示 赠送15款笔刷 29款色卡 一张纸张背景 02 Procre

随机推荐

  • 树莓派Pico

    9 2 在MS Windows上构建 在Microsoft Windows 10或Windows 11上安装工具链与其他平台有些不同 然而安装后 RP2040的构建代码基本类似 Tips 什么是工具链 工具链是指一系列软件 逐个使用这一系列
  • docker run中-v参数的用法解释

    作用 挂载宿主机的一个目录 如 docker run it v 宿主机目录 容器目录 镜像名 bin bash 这里 it是参数作用是 i 以交互模式运行容器 通常与 t 同时使用 t 为容器重新分配一个伪输入终端 通常与 i 同时使用 就
  • keil编译时候产生的错误(Error: L6200E: Symbol....)解决方法

    今天分享一个自己在做实验时候发现的一个错误问题 查了一下网上也有人遇到这样的问题 就拿出来分享了一下自己遇到的情况 首先看keil的错误提示 如图所示 可以看到两个报错为 Error L6200E Symbol usart3 init mu
  • CV Code

    点击我爱计算机视觉标星 更快获取CVML新技术 计算机视觉技术发展迅速 很多时候 可悲的不是我们没有努力 而是没有跟上时代的步伐 努力coding终于出来结果了 却发现早就有人开源了 效果还比自己写的好 CV君汇总了最近过去的一周新出的开源
  • 【Matlab】将Matlab里的几个变量一个存成csv文件

    注意 几个变量类型可以不同 但是长度必须相同 举例说明 1 比如说在workspace里已经有两个变量 a和b如图 每个变量为1列 想把这两个变量存成一个csv文件 2 先给这两个变量名起个名字 分别为A B 存入 datacolumns
  • 数据属性类型

    数据集由数据对象组成 一个数据对象代表一个实体 数据对象又称样本 实例 数据点或对象 属性 attribute 是一个数据字段 表示数据对象的一个特征 属性向量 或特征向量 是用来描述一个给定对象的一组属性 属性有不同类型 标称属性 nom
  • python面试题

    文章目录 Python面试基础题小汇总 1 Python是如何进行内存管理的 2 什么是lambda函数 它有什么好处 3 Python里面如何实现tuple和list的转换 4 请写出一段Python代码实现删除一个list里面的重复元素
  • 常用的运算放大器电路

    在线仿真网站 http scratch trtos com circuitjs html 一 反向比例放大电路 二 同向比例放大电路 三 电压跟随器 四 反向求和运算电路 五 同向求和运算电路 六 加减法运算放大器 七 差分放大器 八 积分
  • 关于自制CMSIS_DAP离线下载器下载算法的代码说明:“0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA“

    关于自制CMSIS DAP离线下载器下载算法的代码说明 0xE00ABE00 0x062D780D 0x24084068 0xD3000040 0x1E644058 0x1C49D1FA 在自制CMSIS DAP离线下载器的时候 利用FLM
  • Mysql篇-第2章,什么是脏读、幻读、不可重复读?如何处理?

    一 Mysql进行事务并发控制时经常遇到的问题 脏读 在事务进行中 读到了其他事务未提交的数据 举个例子 有一个table表 如果执行顺序如下 这种情况下左边查询的结果会是101 正是因为读取到了另一个事务未提交的数据 幻读 在一个事务中
  • selenium 获取cookie 并使用

    selenium 获取cookie 参数设置 以获取阿里云cookie范例 from selenium import webdriver import json url https account aliyun com login logi
  • 使用Python的方式理解Golang的结构体struct

    Go源码 package GoTools import fmt 定义结构体存储密码 type Config struct password string func InitConfig password string Config c ne
  • Vue用户进行页面切换(路由跳转)时,动态改变路由的动画(transition效果)

    当我们在使用Vue Router时 为了用户有更好的视觉效果及体验 我们通常需要实现基于路由的动态过渡效果 github https github com Rise Devin FullStack Product Transport Use
  • retinaface代码讲解_「干货」RetinaFace最强开源人脸识别算法

    看来最早商业化的人脸检测为目标检测算法 依然是各大CV方向AI公司的必争之地 那我们今天主角就是RetinaFace RetinaFace 是今年5月份出现的人脸检测算法 当时取得了state of the art 作者也开源了代码 过去了
  • 集合的知识

    集合 collection集合的常用方法 collection的特点 Collection代表单列集合 每个元素 数据 只包含一个值 Map代表双列集合 每个元素包含两个值 键值对 Collection集合特点 由于collection是一
  • gRpc指南

    本文翻译自官网 原文 https grpc io docs languages java quickstart 快速开始 下面通过一个简单的样例 让你快速上手基于java的gRpc的使用 前置条件 JDK7以上版本 获取示例代码 示例代码是
  • 斯坦福密码学课程-笔记-01-Introduction绪论

    斯坦福密码学课程笔记 01 绪论 Introduction Course Overview Cryptography is everywhere Secure communication Secure Sockets Layer TLS P
  • 使用thop库对yolo等深度学习模型的FLOPS进行计算

    据说yolov5原来的FLOPS计算脚本有bug 因此这个大神推荐使用thop库进行计算 代码如下 input torch randn 1 3 416 416 flops params thop profile model inputs i
  • 【华为OD机试真题 C++】寻找链表的中间结点

    前言 本专栏将持续更新华为OD机试题目 并进行详细的分析与解答 包含完整的代码实现 希望可以帮助到正在努力的你 关于OD机试流程 面经 面试指导等 如有任何疑问 欢迎联系我 wechat steven moda email nansun09
  • SuperMemo 【POJ - 3580】【Splay+懒标记递推想法】

    题目链接 可以说这道题很好的给我们讲述了在Splay树上的lazy标记的递推 跟线段树上类似 在这棵二叉搜索树上 我们一样的去递推懒标记 接下来说说在哪几处需要专门注意懒标记的使用 这里有几处需要注意的地方 就是一开始给你的元素不是已经排好