splay树:hdu4453 Looploop

news2025/1/23 6:58:55

题目链接如下:

Problem - 4453

主要是要对区间操作和这种splay树的性质比较清楚。

关于区间我们设立两个额外节点,用来设立最开始的左右区间。

性质方面,其实就是二叉搜索树的性质,这里的体现就是中序遍历就是顺时针访问输入数据,逆中序就是逆时针。

以下是思路:

我们对每一组输入,首先创建两个节点,用来表示最左最右区间,这样对于后边限定范围十分有帮助。

有几个比较有意思的操作:

1.建树

在这里的建树就是建立一颗二叉搜索树,这个搜索的key是针对输入顺序来说的,而每个树节点又需要表示一些额外的信息比如前驱,孩子节点,以及存储的输入等等。这里还得存储翻转和加标记,一遍后续旋转过程中动态更新。

所以我们最初建立的树是长这样的:

其实这应该是区间操作的核心,就是这个二叉搜索树是左端点和右端点之间的。

2.add

要给顺时针的k2个数加上一个数x,那么我们也就是要从当前指针指向的节点tp往后中序遍历一共k2个几点,把这k2个节点进行加一个数的操作。我们首先把左区间旋转到根的位置,然后把当前指针指向节点旋转到根下面,而这个节点的左边的子树,也就是按照顺时针访问的最后几个元素(逆时针开始的几个),把他给砍掉,再把最右节点的前一个节点转到根,接着把刚刚砍掉的左子树放到最后一个节点的左子树上,也就是当前根节点的右孩子的左孩子上面,这样,在中序遍历的时候,就已经变成了以指针指向的节点为起点,顺时针直到访问到最后一个节点。

这个时候我们还要给当前指针指向的节点到第k2个数都加上一个数x对吧,那么还是老办法,把左区间端点转到根,把第k2+2个数转到root下面,那么此时root的左子树就是[2,k2+1]次序的数,我们把这个树的根打上加的标记,在后续过程动态更新,那么就完成了我们的操作。

而add操作明白了之后,后续的区间操作也就大同小异。

代码如下所示:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<string>

using namespace std;

#define keyv tree[tree[root][1]][0]
#define lt(r) tree[r][0]
#define rt(r) tree[r][1]

const int MAXN=2e5+1;
int pre[MAXN],sz[MAXN],rev[MAXN],add[MAXN],tree[MAXN][2],key[MAXN];
int root,tot1,tot2;
int s[MAXN],a[MAXN];
int n,q,k1,k2;
int tp;

void newnode(int &r,int fa,int k){
    if (tot2) r=s[tot2--];
    else r=++tot1;
    key[r]=k;
    pre[r]=fa;
    add[r]=rev[r]=lt(r)=rt(r)=0;
    sz[r]=1;
}

void update_rev(int r){
    if (!r) return;
    swap(lt(r), rt(r));
    rev[r]^=1;
}

void update_add(int r,int v){
    if (!r) return;
    key[r]+=v;
    add[r]+=v;
}

void pushup(int r){
    sz[r]=sz[lt(r)]+sz[rt(r)]+1;
}

void pushdown(int r){
    if (rev[r]){
        update_rev(lt(r));
        update_rev(rt(r));
        rev[r]=0;
    }
    if (add[r]){
        update_add(lt(r),add[r]);
        update_add(rt(r),add[r]);
        add[r]=0;
    }
}

void build(int &x,int l,int r,int fa){
    if (l>r) return;
    int mid=(l+r)>>1;
    newnode(x,fa,a[mid]);
    build(lt(x),l,mid-1,x);
    build(rt(x),mid+1,r,x);
    pushup(x);
}

void init(){
    root=tot1=tot2=0;
    pre[root]=sz[root]=rev[root]=add[root]=key[root]=lt(root)=rt(root)=0;
    newnode(root,0,-1);
    newnode(rt(root),root,-1);
    for (int i = 0; i < n; ++i) {
        scanf("%d",&a[i]);
//        cout<<a[i]<<"  ";
    }
//    cout<<endl;
    build(keyv,0,n-1,rt(root));
    pushup(rt(root));
    pushup(root);
}

void rotate(int x,int d){
    int y=pre[x];
    pushdown(y);
    pushdown(x);
    tree[y][!d]=tree[x][d];
    pre[tree[x][d]]=y;
    if (pre[y]){
        tree[pre[y]][rt(pre[y])==y]=x;
    }
    pre[x]=pre[y];
    tree[x][d]=y;
    pre[y]=x;
    pushup(y);
}

void splay(int r,int goal){
    pushdown(r);
    while (pre[r]!=goal){
        if (pre[pre[r]]==goal){
            pushdown(pre[r]);
            pushdown(r);
            rotate(r, lt(pre[r])==r);
        }else{
            pushdown(pre[pre[r]]);
            pushdown(pre[r]);
            pushdown(r);
            int y=pre[r];
            int d=(lt(pre[y])==y);
            if (tree[y][d]==r){// 不共线
                rotate(r,!d);
                rotate(r,d);
            }else{
                rotate(y,d);
                rotate(r,d);
            }
        }
    }
    pushup(r);
    if (goal==0) root=r;
}

int getkth(int r,int k){
    pushdown(r);
    int t=sz[lt(r)]+1;
    if (k==t) return r;
    else if (k<t) return getkth(lt(r),k);
    else return getkth(rt(r),k-t);
}

void ADD(int x){
    // 将当前指针指向的位置的左子树去除
    splay(tp,0);
    int tmp=sz[lt(tp)]+1;
    splay(getkth(root,1),0);
    splay(getkth(root,tmp),root);
    tmp=keyv;
    keyv=0;
    pushup(rt(root));
    pushup(root);

    // 将去除的那棵左子树插入到末尾
    splay(getkth(root,sz[root]-1),0);
    keyv=tmp;
    pre[keyv]=rt(root);
    pushup(rt(root));
    pushup(root);

    // 打上加号
    splay(getkth(root,1),0);
    splay(getkth(root,k2+2),root);
    update_add(keyv,x);
    pushup(rt(root));
    pushup(root);

    tp= getkth(root,2);
}

void REVERSE(){
    // 将当前指针指向的位置的左子树去除
    splay(tp,0);
    int tmp=sz[lt(tp)]+1;
    splay(getkth(root,1),0);
    splay(getkth(root,tmp),root);
    tmp=keyv;
    keyv=0;
    pushup(rt(root));
    pushup(root);

    // 将去除的那棵左子树插入到末尾
    splay(getkth(root,sz[root]-1),0);
    keyv=tmp;
    pre[keyv]=rt(root);
    pushup(rt(root));
    pushup(root);

    // 打上逆转标号
    splay(getkth(root,1),0);
    splay(getkth(root,k1+2),root);
    update_rev(keyv);
    pushup(rt(root));
    pushup(root);

    tp= getkth(root,2);
}

void INSERT(int x){
    splay(tp,0);
    int t=sz[lt(root)]+2;
    splay(getkth(root,t),root);
    newnode(keyv, rt(root),x);
    pushup(rt(root));
    pushup(root);
}

void DELETE(){
    splay(tp,0);
    int tmp=sz[lt(root)];
    splay(getkth(root,tmp),0);
    splay(getkth(root,tmp+2),root);

    s[++tot2]=keyv;
    keyv=0;
    pushup(rt(root));
    pushup(root);
    if (tmp+1==sz[root]) tp=getkth(root,2);
    else tp=getkth(root,tmp+1);
}

void MOVE(int x){
    splay(tp,0);
    if (x==1){
        int t=sz[lt(root)];
        if (t==1) tp=getkth(root,sz[root]-1);
        else tp=getkth(root,t);
    }else{
        int t=sz[lt(root)]+2;
        if (t==sz[root]) tp=getkth(root,2);
        else tp=getkth(root,t);
    }
}

int QUERY(){
    splay(tp,0);
    return key[root];
}

char op[100];
int x;
int main()
{
    int kase=0;
    while (cin>>n>>q>>k1>>k2){
        if (!n && !q && !k1 && !k2) break;
        printf("Case #%d:\n",++kase);
        init();
        tp=getkth(root,2);
        while (q--){
            scanf("%s",op);
            if (op[0]=='a'){
                scanf("%d",&x);
                ADD(x);
            }else if (op[0]=='r'){
                REVERSE();
            }else if (op[0]=='i'){
                scanf("%d",&x);
                INSERT(x);
            }else if (op[0]=='d'){
                DELETE();
            }else if (op[0]=='m'){
                scanf("%d",&x);
                MOVE(x);
            }else{
                printf("%d\n",QUERY());
            }
        }
    }
    return 0;
}

最后感谢博客:

HDU 4453 Looploop(伸展树应用,数列环)_Soar-的博客-CSDN博客_hdu-4453

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《统计学习方法》 第十四章 聚类方法

聚类方法 1.聚类是针对给定的样本&#xff0c;依据它们属性的相似度或距离&#xff0c;将其归并到若干个“类”或“簇”的数据分析问题。一个类是样本的一个子集。直观上&#xff0c;相似的样本聚集在同类&#xff0c;不相似的样本分散在不同类。 2.距离或相似度度量在聚类中…

压力传感器

压力传感器 压力传感器是最常用的一种传感器&#xff0c;其应用范围有各种工业互通环境&#xff0c;涉及航空&#xff0c;航天&#xff0c;军工&#xff0c;石化&#xff0c;电力等。按照不同的测试&#xff0c;压力类型可分表压传感器&#xff0c;差压传感器&#xff0c;绝压…

现代密码学导论-19-基于伪随机函数的CPA安全

目录 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造基于伪随机函数的CPA安全的加密方案 THEOREM 3.29 方案3.28是CPA安全的 THEOREM 3.29 的证明 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造…

历史中的密码

角色 发送者、接收者和窃听者 当某个人向另一个人发送信息时&#xff0c;发出信息的人称为发送者&#xff0c;而收到信息的人称为接收者&#xff0c;被发送的信息有时也统称为消息&#xff08; message )。 窃听者 Eve 并不一定是人类&#xff0c;有可能是安装在通信设备上的某…

【JVM】jvm中的方法区简介

jvm中的方法区简介一、JVM体系结构二、方法区是什么&#xff1f;三、方法区能干什么&#xff1f;四、方法区总结一、JVM体系结构 二、方法区是什么&#xff1f; 本文所讲内容在上图中处于运行时数据区内的左侧部分&#xff0c;即 Method Area&#xff08;方法区&#xff09;&a…

REHL7.6静默安装Oracle19C

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

【轨迹跟踪】基于matlab拓展卡尔曼滤波时序四旋翼无人机状态跟踪【含Matlab源码 2246期】

⛄一、拓展卡尔曼滤波时序四旋翼无人机状态跟踪 卡尔曼滤波算法为获得最优估计和最小误差方差&#xff0c;将从目标模型中得到的测量值一步步递推&#xff0c;实时获取新时刻的状态估计值。 假设目标状态方程和观测方程分别为&#xff1a; 其中&#xff0c;k为离散时间&…

投入产出公开数据集:世界投入产出表(1995-2014)、全国投入产出表(1990-2018)、分省市投入产出表(1997-2017)

一、数据介绍 数据名称&#xff1a;世界、全国、各省-投入产出表 数据年份&#xff1a;世界投入产出表(1995-2014)、全国投入产出表(1990-2018)、分省市投入产出表(1997-2017) 数据来源&#xff1a;WIOD、自计算 ① 世界投入产出表&#xff08;1995-2014&#xff09; downlo…

用JSX来写Vue3,瞬间找到React 的感觉

Ⅰ. vue3 的 JSX 写法 对于熟悉react 的小伙伴, 可以通过 jsx 来 做 vue3喜欢 jsx 写法做 vue&#xff0c;代码结构更加美观&#xff0c;让我们一起来踩坑 &#x1f447; 文章目录Ⅰ. vue3 的 JSX 写法Ⅱ. JSX 安装和配置1. 通过 webpack 构建的2. 通过 vite 构建的Ⅳ. JSX 的…

Yocto buildhistory介绍

Yocto buildhistory介绍 在yocto中会频繁的编译修改镜像&#xff0c;当多人多次修改镜像的时候会导致镜像难以维护&#xff0c;我们希望能有一个类似git一样的工具能够显示每次编译的差异性修改&#xff0c;这样当我想要回退到某个日期的某个镜像时能够清晰的知道镜像内部的具…

Dockerfile文件详解

组成部分 说明 基础镜像信息 使用 FROM 关键字指定基础镜像信息&#xff0c;必须是 Dockerfile 文件的第1条指令。 维护者信息 使用 MAINTAINER 关键字指定&#xff0c;可以使用 Dockerfile 文件创建者的姓名或者电子邮件作为维护者信息。 镜像操作指令 每执行一条镜像操…

vue3+Element-plus el-select 下拉选择 多选增加全选封装组件

一、效果图&#xff08;含适用于条件查询组件中使用&#xff09; 二、参数配置 1、代码示例&#xff1a; <t-selectplaceholder"请选择工序"v-model"selectVlaue":optionSource"state.stepList"valueKey"label"change"selec…

部署SpringBoot+Vue3 项目实战,打造企业级在线办公系统

文章目录一、安装docker二、安装2.1. 安装mysql2.2. 安装MongoDB2.3. 安装Redis程序2.4. 安装RabbitMQ2.5. 在云主机上面开放端口三、部署后端项目3.1. 下载JDK镜像3.2. 部署工作流项目3.3. 部署emos-api项目四、在Docker中部署前端项目4.1. 修改前端代码4.2. 打包VUE项目4.3. …

【Hack The Box】linux练习-- time

HTB 学习笔记 【Hack The Box】linux练习-- time &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月24日&#x1f334; &#x1f36d…

Android~Compose脚手架和Toast

系列文章目录 Android~Compose初探Android~Compose之自定义ViewAndroid~Compose相关概念总结Android~Compose脚手架和ToastAndroid~Compose路由Navigation和传参 文章目录系列文章目录目标脚手架基于Snackbar自定义Toast实现效果目标 熟悉Compose中脚手架使用自定义Toast样式…

人工智能轨道交通行业周刊-第24期(2022.11.21-11.27)

追风赶月莫停留&#xff0c;平芜尽处是春山。 --《田歆华夏说》 本期关键词&#xff1a;BIM应用、地铁控制中心、车辆检修智能化、模型轻量化、隧道通风 1 整理涉及公众号名单 1.1 行业类 RT轨道交通中关村轨道交通产业服务平台人民铁道世界轨道交通资讯网铁路信号技术交…

【树莓派不吃灰】Linux篇⑥ 正规表示法与文件格式化处理(核心概念)

目录1. 什么是正则表示法2. 基础正规表示法&#xff08;grep、sed&#xff09;3. 延伸正规表示法4. 文件的格式化与相关处理&#xff08;printf、awk&#xff09;5. 重点回顾❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2022-11…

FPGA实现视频拼接,纯逻辑资源搭建,提供4套工程源码和技术支持

目录1.本方案的实用价值2.总体设计方案3.视频拼接方案算法4.工程1&#xff1a;单路视频输出5.工程2&#xff1a;2路视频拼接输出6.工程3&#xff1a;3路视频拼接输出7.工程4&#xff1a;4路视频拼接输出8.上板调试验证9.福利&#xff1a;工程源码获取1.本方案的实用价值 FPGA实…

【软件测试】测试与开发一对欢喜冤家......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 大伙普遍的看法&…

28.开机默认启动系统-ubuntu和win10

在win10下安装了Ubuntu20.04系统&#xff0c;默认情况下&#xff0c;启动的是Ubuntu系统。 要将默认启动系统设置成win10&#xff0c;方法如下&#xff1a; 1、进入ubuntu系统&#xff0c;按住CtrlAltT键&#xff0c;打开终端。 2、输入命令&#xff1a; sudo gedit /etc/d…