超级记忆节目

news2025/1/26 15:38:36

 一 问题描述

杰克逊被邀请参加电视节目“超强记忆”,参与者会玩一个记忆游戏。主持人先告诉参与者一个数字序列 {A1 , A2 , …, An },然后对该序列执行一系列操作或查询:

① ADD x y D ,表示对子序列 {Ax , …, Ay } 的每个数字都增加 D ,例如在序列 {1, 2, 3, 4, 5} 上执行 ADD 2 4 1,结果为 {1, 3, 4, 5, 5};

② REVERSE x y ,表示反转子序列 {Ax , …, Ay},例如在序列 {1, 2,3, 4, 5} 上执行 REVERSE 2 4,结果为 {1, 4, 3, 2, 5};

③ REVOLVE x y T ,表示旋转子序列 {Ax , …, Ay } T 次,例如在序列 {1, 2, 3, 4,5} 上执行 REVOLVE 2 4 2,结果为 {1, 3, 4, 2, 5};

④ INSERT x P ,表示在 Ax 后插入 P ,例如在序列 {1, 2, 3, 4, 5} 上执行 INSERT 2 4,结果为 {1, 2, 4, 3, 4, 5};

⑤ DELETE x ,表示删除 Ax ,例如在序列 {1, 2, 3, 4, 5} 上执行 DELETE 2,结果为 {1, 3, 4, 5};

⑥ MIN x y,表示查询子序列 {Ax , …, Ay } 的最小数值,例如在序列 {1, 2, 3,4, 5} 上执行 MIN 2 4,结果为 2。

为了使节目更有趣,参与者有机会求助他人。请写一个程序,正确回答每个问题,以便在杰克逊打电话时帮助他。

二 输入

第 1 行输入数字 n(n≤10^5 );接下来输入 n 行描述数字序列;接着输入数字 M(M≤10^5 ),表示操作或查询的数量;然后输入 M 行描述操作或查询。

三 输出

对每个 MIN 查询都输出正确的答案。

四 输入和输出样例

1 输入样例

5

1

2

3

4

5

2

ADD 2 4 1

MIN 4 5

2 输出样例

5

五 分析

本问题涉及 6 种操作:插入、删除、区间查询、区间修改、区间反转、区间旋转,完美诠释了伸展树的神通广大。

六 设计

1 插入

在第 pos 个元素后插入一个元素 val,将 Apos 旋转到根部,再将 Apos+1 旋转到 Apos 下方,最后在 Apos+1 的左子树中插入新节点 val 即可。

2 删除

删除第 pos 个元素,将 pos-1 旋转到根部,再将 Apos+1 旋转到 Apos-1 下方,此时 Apos 就是 Apos+1 的左子树,直接删除即可。

3 区间查询

查询 [l , r] 区间的最小值时,只需将 Al-1 旋转到根,然后将 Ar+1 旋转到 Al-1 的下方,此时需要查询的 [l , r] 区间就是 Ar+1 的左子树,输出该节点的最小值即可。

4 区间修改

和区间查询类似,将 [l , r] 区间的所有元素都增加 val,只需将 Al-1 旋转到根,然后将 Ar+1 旋转到 Al-1 的下方,此时需要增加的 [l , r] 区间就是 Ar+1 的左子树,修改该 [l,r] 区间的根节点(值、区间最小值、懒标记),懒标记会在下次访问时下传。

5 区间反转

和区间查询类似,反转 [l,r] 区间时,只需将 Al-1 旋转到根,然后将 Ar+1 旋转到 Al-1 的下方,此时需要反转的 [l, r ]区间就是 Ar+1 的左子树,在该区间的根节点打上反转懒标记即可。

6 区间旋转

旋转[l,r] 区间 T 次,即将 [l,r] 区间循环右移 T 次,相当于将 [r-T+1,r] 区间的元素移动到 Al-1 之后。

可以将该 [r-T+1,r] 区间暂存后删除,再插入Al-1 之后。首先将 Ar-T 旋转到根,然后将 Ar+1 旋转到 Ar-T 的下方,此时 [r-T+1 , r] 区间就是 Ar+1 的左子树,将其暂存给 tmp 后删除。

然后将 tmp 插入 Al-1 之后。只需将 Al-1 旋转到根,然后将 Al 旋转到 Al-1 的下方,将 tmp 挂接到 Al 的左子树上,即可完成插入操作。

因为 T 有可能超过 [l , r]区间的长度(m=r-l +1),所以只需 T=T%m 。若 T 有可能为负值,则可以通过 T=(T+m )%m 处理。

七 代码

package com.platform.modules.alg.alglib.poj3580;

public class Poj3580 {
    public String output = "";
    private int maxn = 200100;
    private int inf = 0x3f3f3f3f;
    int n, cnt, root; // 结点数,结点存储下标累计,树根
    int a[] = new int[maxn];
    String op;
    private node tr[] = new node[maxn];

    public String cal(String input) {
        int m, l, r, val;
        String[] line = input.split("\n");
        n = Integer.parseInt(line[0]);
        for (int i = 1; i <= n; i++) {
            a[i] = Integer.parseInt(line[i]);
        }

        Init();
        m = Integer.parseInt(line[n + 1]);
        int count = n + 2;
        while (m-- > 0) {
            String[] command = line[count++].split(" ");
            op = command[0];

            if (op.charAt(0) == 'A') {
                l = Integer.parseInt(command[1]);
                r = Integer.parseInt(command[2]);
                val = Integer.parseInt(command[3]);

                Add(++l, ++r, val);
            } else if (op.charAt(0) == 'M') {
                l = Integer.parseInt(command[1]);
                r = Integer.parseInt(command[2]);
                output += Min(++l, ++r) + "\n";
            } else if (op.charAt(0) == 'I') {
                l = Integer.parseInt(command[1]);
                val = Integer.parseInt(command[2]);
                Insert(++l, val);
            } else if (op.charAt(0) == 'D') {
                l = Integer.parseInt(command[1]);

                Delete(++l);
            } else if (op.charAt(0) == 'E') {
                l = Integer.parseInt(command[1]);
                r = Integer.parseInt(command[2]);
                Reverse(++l, ++r);
            } else {
                l = Integer.parseInt(command[1]);
                r = Integer.parseInt(command[2]);
                val = Integer.parseInt(command[3]);
                Revolve(++l, ++r, val);
            }
        }

        return output;
    }


    public Poj3580() {
        for (int i = 0; i < tr.length; i++) {
            tr[i] = new node();
        }
    }

    void Update(int x) {
        tr[x].minv = tr[x].val;
        tr[x].size = 1;
        if (tr[x].son[0] > 0) {
            tr[x].size += tr[tr[x].son[0]].size;
            tr[x].minv = Math.min(tr[x].minv, tr[tr[x].son[0]].minv);
        }
        if (tr[x].son[1] > 0) {
            tr[x].size += tr[tr[x].son[1]].size;
            tr[x].minv = Math.min(tr[x].minv, tr[tr[x].son[1]].minv);
        }
    }

    void Pushdown(int x) {
        if (tr[x].rev == 1) { // 下传翻转标记
            tr[x].rev ^= 1;
            int temp = tr[x].son[0];
            tr[x].son[0] = tr[x].son[1];
            tr[x].son[1] = temp;
            if (tr[x].son[0] > 0)
                tr[tr[x].son[0]].rev ^= 1;
            if (tr[x].son[1] > 0)
                tr[tr[x].son[1]].rev ^= 1;
        }
        if (tr[x].add == 1) { // 下传加标记
            if (tr[x].son[0] > 0) {
                tr[tr[x].son[0]].add += tr[x].add;
                tr[tr[x].son[0]].val += tr[x].add;
                tr[tr[x].son[0]].minv += tr[x].add;
            }
            if (tr[x].son[1] > 0) {
                tr[tr[x].son[1]].add += tr[x].add;
                tr[tr[x].son[1]].val += tr[x].add;
                tr[tr[x].son[1]].minv += tr[x].add;
            }
            tr[x].add = 0;//清除标记
        }
    }

    // 生成新结点
    int New(int father, int val) {
        tr[++cnt].fa = father;
        tr[cnt].val = val;
        tr[cnt].minv = val;
        tr[cnt].size = 1;
        tr[cnt].add = tr[cnt].rev = 0;
        tr[cnt].son[0] = tr[cnt].son[1] = 0;
        return cnt;
    }

    // 旋转
    void Rotate(int x) {
        Pushdown(x);
        int y = tr[x].fa, z = tr[y].fa;
        int c = (tr[y].son[0] == x) ? 1 : 0;
        tr[y].son[1 - c] = tr[x].son[c];
        tr[tr[x].son[c]].fa = y;
        tr[x].fa = z;
        if (z > 0)
            tr[z].son[tr[z].son[1] == y ? 1 : 0] = x;
        tr[x].son[c] = y;
        tr[y].fa = x;
        Update(y);
        Update(x);
    }

    // 将 x 旋转为 goal 的儿子
    void Splay(int x, int goal) {
        while (tr[x].fa != goal) {
            int y = tr[x].fa, z = tr[y].fa;
            if (z != goal) {
                if (((tr[z].son[0] == y ? 1 : 0) ^ (tr[y].son[0] == x ? 1 : 0)) == 0) {
                    Rotate(y);
                } else {
                    Rotate(x);
                }
            }
            Rotate(x);
        }
        // 如果 goal 是 0,则更新根为 x
        if (goal == 0) root = x;
    }

    int Findk(int x, int k) {
        while (true) {
            Pushdown(x);
            int sn = tr[x].son[0] > 0 ? tr[tr[x].son[0]].size + 1 : 1;
            if (k == sn)
                return x;
            if (k > sn) {
                k -= sn;
                x = tr[x].son[1];
            } else
                x = tr[x].son[0];
        }
    }

    // 插入值 val
    void Insert(int pos, int val) {
        int x = Findk(root, pos), y = Findk(root, pos + 1);
        Splay(x, 0);
        Splay(y, x);
        tr[y].son[0] = New(y, val);
        Update(y);
        Update(x);
    }

    // 删除
    void Delete(int pos) {
        int x = Findk(root, pos - 1), y = Findk(root, pos + 1);
        Splay(x, 0);
        Splay(y, x);
        tr[y].son[0] = 0;
        Update(y);
        Update(x);
    }

    // 找 [l,r] 区间最小值
    int Min(int l, int r) {
        int x = Findk(root, l - 1), y = Findk(root, r + 1);
        Splay(x, 0);
        Splay(y, x);
        return tr[tr[y].son[0]].minv;
    }

    int Build(int l, int r, int t, int fa) {
        if (l > r)
            return t;
        int mid = l + r >> 1;
        t = New(fa, a[mid]);
        tr[t].son[0] = Build(l, mid - 1, tr[t].son[0], t);
        tr[t].son[1] = Build(mid + 1, r, tr[t].son[1], t);
        Update(t);
        return t;
    }

    void Init() {
        cnt = root = 0;
        tr[0].son[0] = tr[0].son[1] = 0;
        root = New(0, -inf); // 创建虚结点1
        tr[root].son[1] = New(root, inf); // 创建虚结点2
        tr[root].size = 2;
        tr[tr[root].son[1]].son[0] = Build(1, n, tr[tr[root].son[1]].son[0], tr[root].son[1]);
        Update(tr[root].son[1]);
        Update(root);
    }

    // [l,r] 区间加上 val
    void Add(int l, int r, int val) {
        int x = Findk(root, l - 1), y = Findk(root, r + 1);
        Splay(x, 0);
        Splay(y, x);
        tr[tr[y].son[0]].val += val;
        tr[tr[y].son[0]].minv += val;
        tr[tr[y].son[0]].add += val;
        Update(y);
        Update(x);
    }

    // [l,r] 区间翻转
    void Reverse(int l, int r) {
        int x = Findk(root, l - 1), y = Findk(root, r + 1);
        Splay(x, 0);
        Splay(y, x);
        tr[tr[y].son[0]].rev ^= 1; // 加翻转标记
    }

    // 偏移 T 位
    void Revolve(int l, int r, int T) {
        T %= r - l + 1;
        if (T == 0) return;
        int x = Findk(root, r - T), y = Findk(root, r + 1);
        Splay(x, 0);
        Splay(y, x);
        int tmp = tr[y].son[0];
        tr[y].son[0] = 0;
        Update(y);
        Update(x);
        x = Findk(root, l - 1);
        y = Findk(root, l);
        Splay(x, 0);
        Splay(y, x);
        tr[y].son[0] = tmp;
        tr[tmp].fa = y;
        Update(y);
        Update(x);
    }
}

class node {
    int son[] = new int[2];//左右孩子0,1
    int val, fa; // 值,父亲
    int minv; // 最小值
    int size, add, rev; // 大小,加标记,翻转标记
}

八 测试

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

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

相关文章

Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤

Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤 目录Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤1、简介2、软件下载1、Java SDK2、Android SDK3、Android NDK3、软件部署4、测试1、简介 搭建Qt For Android开发环境需要安装的软件有&#xff1a; JAVA …

第十四届蓝桥杯模拟赛(第二期)

写在前面 包含本次模拟赛的10道题题解能过样例&#xff0c;应该可以AC若有错误&#xff0c;欢迎评论区指出有疑问可私信我哈&#x1faf0;&#x1f3fb;从2023开始暴力枚举每一个数&#xff0c;直到找到正确答案 start 2022def check(num) :t str(bin(num))if t[-6:] 0000…

核函数简介

文章目录基本概念概念1概念2:Kernel Func总结内积矩阵&#xff08;Gram/Kernel Matrix&#xff09;一些思考什么是有限正半定常用的Kernel FunctionsLinear KernelPolynomial KernelRBF(Gaussian) Kernel基本概念 概念1 高维空间存在可分的情况。 我们可以找一个映射函数送过…

【C++】vector的模拟实现

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;前言&…

程序员5分钟,带你看完24岁60W年薪架构师的简历,上面竟然写着精通JVM

前言 近期&#xff0c;看了一份24岁60W年薪架构师简历&#xff0c;上面写着他的求职意向所掌握的技能....... 所掌握的技能大部分写的都是精通&#xff01;我不禁想问&#xff0c;大佬都这么强吗&#xff1f;你敢在简历上把所有的技能都写精通吗&#xff1f; 简历 下面来带…

matlab学习笔记(六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 matlab学习笔记&#xff08;六&#xff09;一、信号基本运算的MATLAB实现二、计算两信号的卷积积分卷积积分三、两序列的卷积和一、信号基本运算的MATLAB实现 信号基本运算是…

pdf编辑软件哪个好?编辑pdf的软件分享一款,像word一样好用!

编辑文档时&#xff0c;很多人习惯用word及pdf进行办公&#xff0c;而使用中&#xff0c;经常会发现word和pdf之间&#xff0c;总是无法满足我们的切换需要。如果掌握一款可以编辑pdf的软件&#xff0c;像word一样简单使用&#xff0c;又能满足word的各种功能所需&#xff0c;那…

代理,反射,AOP

这篇文章主要讲三个点 1.设计模式中的代理模式 2.JAVA中的反射&#xff0c;因为用到了动态代理&#xff0c;这里举一下JDK代理和GCLIB代理的例子 3.介绍一下spring的aop是怎么用到了代理 1.设计模式中的代理模式 代理模式解决的问题&#xff1a; 在直接访问对象时带来的问题&am…

2022C语言知识点大全【详细、必备】

C语言期末必背知识点汇总【全】C语言最重要的知识点&#xff08;一&#xff09;《图片彩版》《C语言程序设计》必背基本知识点&#xff08;二&#xff09;C语言程序设计复习资料&#xff08;三&#xff09;C语言最重要的知识点&#xff08;一&#xff09;《图片彩版》 建议收藏…

Linux | 进程间通信 | system V共享内存 | 介绍和使用

文章目录system V共享内存介绍共享内存的创建shmget共享内存的获取shmctl&#xff0c;可用于共享内存的删除shmat && shmdt共享内存的使用Linux对system V的设计思路system V共享内存介绍 进程间通信的前提是&#xff1a;使不同进程看到同一份资源&#xff0c;在使用匿…

循环中的闭包

目录 1. 什么是闭包&#xff1f;闭包的作用&#xff1f; 1.1 可以访问 外部作用域 中变量的内部函数 1.2 闭包可以访问外部作用域中的变量及传参 2. 异步操作中 变量 的生命周期&#xff0c;取决于 闭包 的生命周期 2.1 Timer 定时器&#xff08;保留到 定时器回调执行完…

阿里内部总结的微服务笔记,从入门到精通,初学者也能学的会

前言 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致系统架构也在不断的进行变化。 一、系统架构演变 从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程: 单体应用架构--->垂直应用架构--->分布式架构--->SOA 架构-…

Flutter 单元测试例子

Flutter 单元测试例子 原文 https://medium.com/app-dev-community/flutter-unit-testing-with-simple-examples-9c07499e4079 前言 执行单元测试来验证软件的每个组件。因此&#xff0c;我们需要尽可能多地测试每个单独的微 widget 。这些都是由开发人员在开发阶段完成的。单元…

回归分析(2) 一元回归模型

如上所述&#xff0c;为了易于确定回归函数μ(x)中的未知参数&#xff0c;我们来讨论变量Y与x之间存在着线性相关关系的情形 散布在某一条直线的周围&#xff0e;于是&#xff0c;我们可以用线性回归方程 来描述Y与x之间的相关关系&#xff0c;并假设相应的误差&#xff08;称为…

Go-zero框架学习+xorm+gorm配置

Go-zero框架学习xormgorm配置 文章目录Go-zero框架学习xormgorm配置一、框架介绍二、go-zero快速搭建1.下载go-zero2.安装goctl3.项目初始化4.测试5.项目结构6.快速生成rpc服务7.快速添加api接口8.快速生成model服务8.快速生成Dockerfile9.快速生成K8s部署文件三.golang的ORM框…

[go学习笔记.第十七章.redis的使用] 1.redis的使用

1.redis基本介绍 (1).Redis 是 NoSQL 数据库&#xff0c;不是传统的关系型数据库,官网: https://redis.io/ 和http://redis.cn/ (2).Redis: REmote Dlctionary Sever&#xff08;远程字典服务器&#xff09;, Redis 性能非常高&#xff0c;单机能够达到 15w qps,通常适合做缓存…

刷爆力扣之盛最多水的容器

刷爆力扣之盛最多水的容器 HELLO&#xff0c;各位看官大大好&#xff0c;我是阿呆 &#x1f648;&#x1f648;&#x1f648; 今天阿呆继续记录下力扣刷题过程&#xff0c;收录在专栏算法中 &#x1f61c;&#x1f61c;&#x1f61c; 该专栏按照不同类别标签进行刷题&#xff…

安卓的分区一点有用知识:super、lpunpack、lpdump

我们知道这个安卓的镜像分区有很多个。 那么这个文章要介绍什么呢&#xff1f; 三个点&#xff1a; 一是现在的android支持动态分区&#xff0c;很多的东西都被放到super分区里面了&#xff0c;这个应该是可以配置的。然后super里面有比如system、vendor这种比较大的分区。那…

教务排课系统毕业设计,大学排课系统设计与实现,排课系统论文作品参考

功能清单 【后台管理员功能】 录入分院&#xff1a;录入分院名称&#xff0c;简介&#xff0c;详情 分院管理&#xff1a;管理已经录入分院&#xff0c;支持修改和删除 老师录入&#xff1a;录入老师姓名、联系方式、头像、老师简介 老师管理&#xff1a;管理所有已经录入老师…

基于Matlab模拟用于海况海洋学研究的 X 波段雷达系统(附源码)

目录 一、定义雷达系统参数 二、对海面进行建模 三、配置雷达收发器 四、生成数据多维数据集 五、处理海面回波 六、总结 七、程序 海事雷达系统在充满挑战的动态环境中运行。为了改进对感兴趣目标的检测并评估系统性能&#xff0c;必须了解海面返回的性质。 在本例中&a…