玩链子游戏

news2025/2/27 14:13:32

一 游戏描述

有一条链子,上面有 n 颗钻石,钻石编号为 1~n 。可以对该链子执行两种操作:

① CUT a b c (区间切割操作)

切下从第 a 颗钻石到第 b 颗钻石的链子,把它插在剩余链子的第 c 颗钻石后面;比如 n 等于8,链子是 1, 2, 3, 4, 5, 6, 7, 8,对该链子执行 CUT 3 5 4,会切下 3, 4, 5 链子,剩下 1, 2, 6, 7, 8 链子,把 3, 4, 5 链子插入第 4 颗钻石之后,现在的链子是 1, 2, 6, 7,3, 4, 5, 8;

② FLIP a b (区间反转操作)

切下从第 a 颗钻石到第 b 颗钻石的链子,把链子倒过来放回原来的位置,比如在链子 1, 2,6, 7, 3, 4, 5, 8 上执行 FLIP 2 6,则得到的链子是1, 4, 3, 7, 6,2, 5, 8。那么执行 m 种操作后,链子的外观是怎样的呢?

二 输入

输入包括多个测试用例,在测试用例的第 1 行都输入两个数字 n 和 m(1≤n ,m≤3×10^5 ),分别表示链子的钻石总数和操作次数。接下来的 m 行,每行都输入 CUT a b c 或者 FLIP a b 。CUT a b c 表示切割操作,1≤a≤b≤n,0≤c≤n-(b-a +1);FLIP a b 表示反转操作,1≤a<b≤n 。输入结束的标志是两个-1,不做处理。

三 输出

对每个测试用例,都输出一行 n 个数字,第 i 个数字是链子上第 i 颗钻石的编号。

四 输入和输出样例

1 输入样例

8 2

CUT 3 5 4

FLIP 2 6

-1 -1

2 输出样例

1 4 3 7 6 2 5 8

五 分析

本游戏包括区间切割(CUT a b c )、区间反转(FLIP a b)两种操作。这里以输入测试用例“8 2”进行分析,过程如下。

1 链子的初始状态如下

2 执行 CUT 3 5 4

切下从第 3 颗钻石到第 5 颗钻石的链子,把它插入剩余链子上第4颗钻石的后面。

3 执行 FLIP 2 6

切下从第 2 颗钻石到第 6 颗钻石的链子,把它倒过来放回原来的位置。

六 算法设计

1 创建伸展树,因为要进行区间操作,因此增加两个虚节点。

2 根据读入的信息,判断是执行区间切割操作还是区间反转操作。

3 按照中序遍历,输出伸展树。

七 图解

1 创建伸展树

数据序列为 1, 2, 3, 4, 5, 6, 7, 8,创建伸展树时在序列的首尾增加两个虚节点,该节点的值为无穷小 -inf,无穷大 inf(inf 是一个较大的数,如 0x3f3f3f3f)。由于在序列前面添加了一个虚节点,所以原来的位序增加 1,因此对 [l , r ]区间的操作变为对 [++l ,++r] 区间的操作。

2 区间切割

首先切割 [l , r] 区间,将 Al-1 旋转到根,然后将Ar +1 旋转到 Al -1 的下方,此时切割的 [l , r ] 区间是 Ar +1 的左子树,用 tmp 暂存。然后查找第 pos 个节点,可以将 Apos 旋转到根部,再将 Apos+1 旋转到 Apos 下方,将 tmp 挂接在 Apos+1 的左子树上即可,相当于将 [l,r ] 区间插入第 pos 个节点之后,如下图所示。

CUT 3 5 4:因为添加了虚节点,因此切割 [4, 6] 区间,将其插入剩余区间第 5 个节点的后面。将第 3个节点(数字2)旋转到根,然后将第 7 个节点(数字6)旋转到第 3 个节点的下方,此时切割的区间就是第 7 个节点的左子树,用 tmp 暂存。

然后将第 5 个节点旋转到根部,将第 6 个节点旋转到其下方,将 tmp 挂接在第 6 个节点的左子树上即可,此时序列为1, 2, 6, 7, 3, 4, 5,8,如下图所示。

3 区间反转

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

八 代码

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

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

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

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

    // 下传翻转标记
    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;
        }
    }

    // 生成新结点
    int New(int father, int val) {
        tr[++cnt].fa = father;
        tr[cnt].val = val;
        tr[cnt].size = 1;
        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];
        }
    }

    int Build(int l, int r, int t, int fa) {
        if (l > r)
            return t;
        int mid = l + r >> 1;
        t = New(fa, 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]区间切割,插入第c个之后
    void Cut(int l, int r, int c) {
        int x = Findk(root, l - 1);
        int 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, c);
        y = Findk(root, c + 1);
        Splay(x, 0);
        Splay(y, x);
        tr[y].son[0] = tmp;
        tr[tmp].fa = y;
        Update(y);
        Update(x);
    }

    // [l,r]区间翻转
    void Flip(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; // 加翻转标记
    }

    // 中序遍历测试
    void Print(int k) {
        Pushdown(k);
        if (tr[k].son[0] > 0)
            Print(tr[k].son[0]);
        if (tr[k].val != -inf && tr[k].val != inf) {
            if (flag) {
                output += tr[k].val;
                flag = false;
            } else
                output += " " + tr[k].val;
        }
        if (tr[k].son[1] > 0)
            Print(tr[k].son[1]);
    }

    public String cal(String input) {
        int m, a, b, c;
        String[] line = input.split("\n");
        String[] num = line[0].split(" ");
        n = Integer.parseInt(num[0]);
        m = Integer.parseInt(num[1]);

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


            if (op.charAt(0) == 'C') {
                a = Integer.parseInt(command[1]);
                b = Integer.parseInt(command[2]);
                c = Integer.parseInt(command[3]);
                Cut(++a, ++b, ++c);
            } else if (op.charAt(0) == 'F') {
                a = Integer.parseInt(command[1]);
                b = Integer.parseInt(command[2]);
                Flip(++a, ++b);
            }
        }
        flag = true;
        Print(root);
        return output;
    }
}

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

九 测试

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

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

相关文章

【食品加工技术】第五章 烘烤食品加工技术 笔记

【食品加工技术】第五章 烘烤食品加工技术 笔记5.1 焙烤食品概述烘烤食品的分类按发酵和膨化程度分类安装生产工艺分类烘烤食品的原料面粉糖蛋品乳及乳制品膨松剂烘烤设备常用设备恒温设备常用工具5.2 面包加工工艺和关键技术面包的分类面包的发酵原理面包的工艺流程一次发酵二…

uboot引导应用程序

uboot默认是支持执行应用程序的&#xff0c;就像引导内核一样&#xff0c;我们也可以自己写一个应用程序&#xff0c;让uboot启动时引导。 在uboot examples/standalone 目录下&#xff0c;有hello_world.c文件&#xff0c;编译uboot的时候&#xff0c;会自动编译hello_world.…

详解 InnoDB Cluster 主机名问题

详解 InnoDB Cluster 主机名问题 文章目录详解 InnoDB Cluster 主机名问题导言测试过程结论导言 因在写 【InnoDB Cluster】修改已有集群实例名称及成员实例选项 时发现主机名这块有一些问题&#xff0c;在其中进行了部分测试&#xff0c;但为使其内容精简&#xff0c;故将此部…

程序员必知的三款在线绘图工具

文章目录2.draw.io3.Lucidchart4.PrcessOn5.小结正所谓“一图胜千言”&#xff0c;无论是商务办公、PPT 演示、学习总结、技术交流、项目开发&#xff0c;我们常常都需要制作一些图表、流程图、架构图来更直观地呈现内容以及归类整理知识点。 今天就来说下程序员们常用的三款在…

【矩阵论】正规方程——生成子空间

5.1 子空间 5.1.1. 定义 设 W⊂CnW\subset C^nW⊂Cn &#xff0c;即子空间对线性组合封闭 若(1)对∀α,β∈W&#xff0c;有αβ∈W(对加法封闭)(2)对∀α∈W,∀k∈C&#xff0c;有kα∈W(对数乘封闭)\begin{aligned} 若 &(1)对\forall \alpha,\beta\in W&#xff0c;有\…

秋招失利,拿到这份“Java 高分指南(25 专题)”,金三银四翻盘有望

面试造火箭&#xff0c;工作拧螺丝&#xff01;金九银十灰溜溜地落榜&#xff0c;备受打击。正当准备明年金三银四之际&#xff0c;意外喜提朋友赠送的这“Java 高分指南&#xff08;25 专题&#xff09;”&#xff1a;Elasticsearch、微服务、Linux、JavaOOP、集合/泛型、Mysq…

Flutter高仿微信-第57篇-添加好友

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; /*** Author : wangning* Email : maoning20080809163.c…

ThreadLocal

文章目录一、ThreadLocal是什么二、ThreadLocal作用三、ThreadLocal的设计结构早期:现在:四、ThreadLocal核心方法1. set方法2. get方法3. remove方法五、ThreadLocal内存泄漏六、使用场景七、参考资料前言&#xff1a; 再写博客时&#xff0c;遇到了如何处理保存用户的信息时出…

基于共享储能电站的工业用户 日前优化经济调度matlab程序(yalmip+cplex)(yalmip+gurobi)

基于共享储能电站的工业用户 日前优化经济调度matlab程序&#xff08;yalmipcplex&#xff09;&#xff08;yalmipgurobi&#xff09; 参考文献&#xff1a;基于共享储能电站的工业用户 日前优化经济调度 摘要: 文章提出一种基于共享储能电站的工业用户日前优化经济调度方法。…

nginx反向代理,负载均衡配置

文章目录一.nginx代理简介二.nginx配置简介三.nginx作为反向代理的配置四.nginx作为负载均衡的配置五.使用nginx代理的坑一.nginx代理简介 其实nginx作为代理有两种 正向代理: 隐藏客户端的信息;如科学上网 反向代理: 隐藏服务端的信息;如负载均衡 二.nginx配置…

11.26

目录 一.做题出错 1. 2.数组长度的一半 3.选择题 二.优先级队列(堆) 1.二叉树的顺序存储 1.1 存储方式 1.2下标关系 2.堆(heap) 2.1概念 2.2 操作-向下调整 三 建堆 四.优先级队列 1 概念 2 内部原理 3.操作-入队 offer() 4.操作-出队 五.计算糖果 一.做题出错…

docker如何下载国外镜像

目录背景解决方案1、创建阿里云镜像仓库2、使用https://labs.play-with-docker.com下载镜像3、将镜像上传到阿里云镜像仓库4、从阿里云镜像仓库中拉取镜像到我们linux系统中5、改变我们linux系统中拉取的镜像名称背景 今天在安装grafana和prometheus&#xff0c;但是在下载下面…

Java数据结构

目录 1、栈 2、队列 3、数组 4、链表 5、树 7、平衡二叉树 8、红黑树 1、栈 特点&#xff1a;先进后出&#xff0c;后进先出 数据进入栈模型的过程称为:压/进栈 数据离开栈模型的过程称为:弹/出栈 2、队列 特点&#xff1a;先进先出&#xff0c;后进后出 数据从后…

MyBatis-Plus中的更新操作(通过id更新和条件更新)

目录 前言 一、通过id更新 二、条件更新 2.1 使用QueryWrapper进行条件更新 2.2 使用UpdateWrapper进行条件更新 总结 前言 本文学习MP中的更新操作方法&#xff0c;带大家一起查看源码&#xff0c;了解更新操作的方法。学会熟练地去运用更新方法解决自己在项目中的问题…

Linus 文件处理(三)

目录 一、前言 二、扫描目录 1、opendir 2、readdir 3、telldir 4、seekdir 5、 closedir 6、A Directory-Scanning Program 三、Errors 1、strerror 2、perror 一、前言 本文将简单介绍Linux文件和目录&#xff0c;以及如何操作它们&#xff08;如何创建文件、打开…

独家 | 使用python马尔科夫链方法建模星巴克等待时长

作者&#xff1a;Piero Paialunga翻译&#xff1a;陈超校对&#xff1a;和中华本文约4200字&#xff0c;建议阅读11分钟本文使用马尔科夫链的方法对星巴克购买咖啡的等待时长进行建模。以下内容关于如何使用马尔科夫链计算你在星巴克咖啡的等待时长。图片来自Unplash&#xff0…

Spring - ApplicationContextInitializer 扩展接口

文章目录Preorg.springframework.context.ApplicationContextInitializer扩展点扩展接口扩展生效方式方式一 &#xff1a; Spring SPI扩展方式二 &#xff1a; 配置文件方式三 &#xff1a;启动类手工add测试结果Pre Spring Boot - 扩展接口一览 org.springframework.context.…

详解诊断数据库ODX-C

文章目录 前言一、ODX—C作用是什么?二、ODX-C数据库在工具ODXStudio的编辑方法总结前言 ODX是全球通用的一种诊断数据库格式,相比CDD文件(Vector公司私有的一种数据库格式),应用场景和范围更广,包含了不同的子类: ODX-C\-D\-V\-E\-F\-FD 今天这篇文章仅对ODX-C做一个…

开源物联网系统 ThingsBoard 上手

开源物联网系统 ThingsBoard 上手 centos yum 被占用问题解决&#xff1a; 描述&#xff1a;Another app is currently holding the yum lock; waiting for it to exit 参考&#xff1a;https://blog.csdn.net/Dan1374219106/article/details/112450922 查看yum占用&#xff1a…

制作一个简单HTML中华传统文化网页(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…