[NOI 2003] 文本编辑器 Splay 维护序列 / 块状链表

2023-05-16

传送门(JZOJ)
(第一道全国决赛题)

解法 1:使用 Splay 维护
不管怎么说,总和刚刚学过的迎合上了。这道题可以直接上 Splay 维护线性序列,光标位置用一个变量存就可以了。
这次写 Splay 时犯的错误:
1.一个地方的 null 写成了 NULL
就没有了。

但是最后有一个点 Runtime Error 了,经调试,发现是 select 时栈溢出了,说明 Splay 退化成了一条链。但是这个测试点操作并不多,一开始我还建成了一棵完全二叉树,为什么会溢出呢?

数据大概长这样
Insert 2 ^ 20
Delete 2 ^ 20

观察发现,我的输出字符串是通过不断地旋转根结点寻找后继来输出的,因为这个时间是 O(1) 的,所以我就这么写了。栈溢出,说明这样从头到尾 select 后,Splay 退化成了链状。以后可不能这么遍历序列了。
正确的做法是:把中间要输出的序列 split 出来,中序遍历后再 merge。这样就能防止退化了。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
typedef int INT;
using std::cin;
using std::cout;
using std::endl;
inline INT readIn()
{
    INT a = 0;
    bool minus = false;
    char ch = getchar();
    while (!(ch == '-' || (ch >= '0' && ch <= '9'))) ch = getchar();
    if (ch == '-')
    {
        minus = true;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        a = a * 10 + (ch - '0');
        ch = getchar();
    }
    if (minus) a = -a;
    return a;
}
inline void printOut(INT x)
{
    char buffer[20];
    INT length = 0;
    bool minus = x < 0;
    if (minus) x = -x;
    do
    {
        buffer[length++] = x % 10 + '0';
        x /= 10;
    } while (x);
    if (minus) buffer[length++] = '-';
    do
    {
        putchar(buffer[--length]);
    } while (length);
}

INT q;

class Splay
{
    struct Node
    {
        char v;
        INT s;
        Node* ch[2];
        Node() : v(), s(), ch() {}
        void maintain()
        {
            s = ch[0]->s + ch[1]->s + 1;
        }
        INT comp(INT k)
        {
            if (k == ch[0]->s + 1) return -1;
            return ch[0]->s + 1 < k;
        }
    };
    Node *null;
    Node *root;

public:
    Splay()
    {
        null = new Node;
        null->ch[0] = null->ch[1] = null;
        root = new Node;
        root->ch[1] = new Node;
        root->ch[0] = root->ch[1]->ch[0] = root->ch[1]->ch[1] = null;
        root->ch[1]->maintain();
        root->maintain();
    }

private:
    void rotate(Node* &r, INT d)
    {
        Node* k = r->ch[d ^ 1];
        r->ch[d ^ 1] = k->ch[d];
        k->ch[d] = r;
        r->maintain();
        k->maintain();
        r = k;
    }
    void select(Node* &r, INT k)
    {
        INT d = r->comp(k);
        if (d == -1) return;
        INT k1 = k - d * (r->ch[0]->s + 1);
        Node* &p = r->ch[d];
        INT d2 = p->comp(k1);
        INT k2 = k1 - d2 * (p->ch[0]->s + 1);
        if (d2 != -1)
        {
            select(p->ch[d2], k2);
            if (d == d2)
                rotate(r, d ^ 1);
            else
                rotate(p, d2 ^ 1);
        }
        rotate(r, d ^ 1);
    }

public:
    void wander(INT l, INT r)
    {
        Node* mid = split(root, l + 1);
        Node* right = split(mid, r - l + 1);
        wander(mid);
        merge(root, merge(mid, right));
    }
private:
    void wander(Node* r)
    {
        if (r == null) return;
        wander(r->ch[0]);
        putchar(r->v);
        wander(r->ch[1]);
    }
private:
    Node* split(Node* &r, INT k)
    {
        select(r, k);
        Node* ret = r->ch[1];
        r->ch[1] = null;
        r->maintain();
        return ret;
    }
    Node* merge(Node* &l, Node* r)
    {
        select(l, l->s);
        l->ch[1] = r;
        l->maintain();
        return l;
    }
public:
    void erase(INT l, INT r)
    {
        Node* mid = split(root, l + 1);
        Node* right = split(mid, r - l + 1);
        merge(root, right);
    }
public:
    void insert(char* str, INT k)
    {
        INT length = strlen(str);
        Node* ins = null;
        make(ins, 0, length, str);
        Node* right = split(root, k + 1);
        merge(root, merge(ins, right));
    }
private:
    void make(Node* &node, INT l, INT r, char* str)
    {
        if (l >= r) return;
        node = new Node;
        node->ch[0] = node->ch[1] = null;
        INT mid = (l + r) >> 1;
        node->v = str[mid];
        make(node->ch[0], l, mid, str);
        make(node->ch[1], mid + 1, r, str);
        node->maintain();
    }
};

char buffer[2 * 1024 * 1024 + 5];
void run()
{
    q = readIn();
    Splay editor;
    INT pos = 0;
    while (q--)
    {
        char ins[10];
        scanf("%s", ins);
        switch (ins[0])
        {
        case 'M':
        {
            pos = readIn();
            break;
        }
        case 'I':
        {
            INT length = readIn();
            for (int i = 0; i < length; i++)
            {
                char ch = getchar();
                while (!(ch >= 32 && ch <= 126)) ch = getchar();
                buffer[i] = ch;
            }
            buffer[length] = '\0';
            editor.insert(buffer, pos);
            break;
        }
        case 'D':
        {
            INT length = readIn();
            editor.erase(pos, pos + length - 1);
            break;
        }
        case 'G':
        {
            INT length = readIn();
            editor.wander(pos, pos + length - 1);
            putchar('\n');
            break;
        }
        case 'P':
        {
            pos--;
            break;
        }
        case 'N':
        {
            pos++;
            break;
        }
        default:
            break;
        }
    }
}

int main()
{
    run();
    return 0;
}

解法 2:使用块状链表维护
块状链表的使用详细见专题,这里只贴个代码(C++ 11)

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <bitset>
typedef int INT;
using std::cin;
using std::cout;
using std::endl;
inline INT readIn()
{
    INT a = 0;
    bool minus = false;
    char ch = getchar();
    while (!(ch == '-' || (ch >= '0' && ch <= '9'))) ch = getchar();
    if (ch == '-')
    {
        minus = true;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        a = a * 10 + (ch - '0');
        ch = getchar();
    }
    if (minus) a = -a;
    return a;
}
inline void printOut(INT x)
{
    char buffer[20];
    INT length = 0;
    bool minus = x < 0;
    if (minus) x = -x;
    do
    {
        buffer[length++] = x % 10 + '0';
        x /= 10;
    } while (x);
    if (minus) buffer[length++] = '-';
    do
    {
        putchar(buffer[--length]);
    } while (length);
}

class BlockList
{
    static const INT size = 5000;
    struct Block
    {
        INT s;
        char v[size];
        Block() : s() {}
        Block(char* begin, char* end) : s(), v()
        {
            s = end - begin;
            std::copy(begin, end, v);
        }
    };
    std::list<Block> blocks;
    typedef std::list<Block>::iterator IT;
    static IT Next(IT it) { IT ret = it; return ++ret; }

    IT locate(INT& k)
    {
        IT it;
        for (it = blocks.begin(); it != blocks.end(); it++)
        {
            if (k <= it->s)
                return it;
            k -= it->s;
        }
        return it;
    }
    void maintain()
    {
        IT it;
        for (it = blocks.begin(); it != blocks.end(); it++)
        {
            IT next = Next(it);
            while (next != blocks.end() && it->s + next->s <= size)
            {
                merge(it, next);
                next = Next(it);
            }
        }
    }
    void merge(IT l, IT r)
    {
        std::copy(r->v, r->v + r->s, l->v + l->s);
        l->s += r->s;
        blocks.erase(r);
    }
    bool split(IT it, INT k)
    {
        if (k >= it->s) return false;
        blocks.insert(Next(it), Block(it->v + k, it->v + it->s));
        it->s = k;
        return true;
    }

public:
    void insert(char* begin, char* end, INT k)
    {
        IT it = locate(k);
        if (it != blocks.end())
        {
            split(it, k);
            it++;
        }
        while (end - begin >= size)
        {
            blocks.insert(it, Block(begin, begin + size));
            begin += size;
        }
        if (end - begin)
        {
            blocks.insert(it, Block(begin, end));
        }
        maintain();
    }
    void erase(INT k, INT s)
    {
        IT it = locate(k);
        split(it, k);
        it++;
        IT next = it;
        while (s > 0)
        {
            next++;
            if (it->s < s)
            {
                s -= it->s;
                blocks.erase(it);
            }
            else
            {
                split(it, s);
                s -= it->s;
                blocks.erase(it);
            }
            it = next;
        }
        maintain();
    }
    typedef void(*Func)(const char& v);
    void wander(INT k, INT s, Func op)
    {
        IT it = locate(k);
        k--;
        while (s--)
        {
            op(it->v[k++]);
            if (k >= it->s)
            {
                it++;
                k = 0;
            }
        }
    }
};

char buffer[2 * 1024 * 1024 + 5];
void run()
{
    INT q = readIn();
    BlockList editor;
    INT pos = 0;
    while (q--)
    {
        char ins[10];
        scanf("%s", ins);
        switch (ins[0])
        {
        case 'M':
        {
            pos = readIn();
            break;
        }
        case 'I':
        {
            INT length = readIn();
            for (int i = 0; i < length; i++)
            {
                char ch = getchar();
                while (!(ch >= 32 && ch <= 126)) ch = getchar();
                buffer[i] = ch;
            }
            editor.insert(buffer, buffer + length, pos);
            break;
        }
        case 'D':
        {
            INT length = readIn();
            editor.erase(pos, length);
            break;
        }
        case 'G':
        {
            INT length = readIn();
            editor.wander(pos + 1, length, [](const char& ch) { putchar(ch); });
            putchar('\n');
            break;
        }
        case 'P':
        {
            pos--;
            break;
        }
        case 'N':
        {
            pos++;
            break;
        }
        default:
            break;
        }
    }
}

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

[NOI 2003] 文本编辑器 Splay 维护序列 / 块状链表 的相关文章

  • SQL语句

    Select SQL 执行顺序 fromonjoinwheregroup byhavingselectdistinctorder bylimit WHERE 字段比较 代码作用 61 等于 lt gt 61 不等于 lt lt 61 小于
  • Bootstrap笔记

    Bootstrap样式 CSS导入 span class token tag span class token tag span class token punctuation lt span link span span class to
  • JSTL与EL表达式

    什么是JSTL JSTL是对EL表达式的扩展 xff0c JSTL是标签语言 xff01 规范了每个标签的职责范围 JSTL标签库 core 核心标签库 fmt 格式化标签库 导标签包 span class token operator l
  • Ubuntu中/usr/local 和 ~/.local 之间的区别

    Ubuntu中 usr local 和 local 之间的区别 usr local 是一个可供所有用户使用的软件可由管理员安装的地方 local bin 是一个用户可以安装软件供自己使用的地方 不同发行版和社区使用的目录结构的历史有些混乱
  • Centos7配置yum镜像源(base,extras,updates,epel,local)

    一 备份默认源 由于默认源都在国外 xff0c 速度非常慢 xff0c 需要把默认的源配置文件备份后删除 span class token comment 进入配置文件目录 span span class token function cd
  • win10彻底关闭windows update 自动更新的方法

    转载自 xff1a https jingyan baidu com article 6181c3e0d75aaa152ef15326 html 其实保留更新还是很有用的 xff0c 毕竟官方一直在修复漏洞 但是服务器虚拟机中运行的win10
  • 解决Centos 7 VNC黑屏

    在配置Centos 7下VNC时发现root用户可以正常登陆VNC桌面 xff0c 而普通用户VNC桌面黑屏 xff0c 分析 vnc xstarup 后发现是普通用户没有执行 etc X11 xinit xinitrc的权限 bin sh
  • 一个与cni0相关的pod创建问题

    今天查看k8s xff0c 发现有个coredns的pod创建失败 xff0c 查看这个POD的信息 xff0c 显示如下错误 combined from similar events Failed to create pod sandbo
  • Debian10安装SSH、配置NTP、安装配置UFW防火墙、配置PATH

    一 SSH安装配置 1 1 安装SSH span class token comment 安装SSH客户端 span apt span class token function install span openssh client spa
  • Debian10 创建用户、用户组、切换用户

    span class token comment 新建用户组 span span class token function groupadd span hausers span class token comment 新建用户并加入用户组
  • C++关于循环依赖的问题

    C 43 43 关于循环依赖的问题 xff1a 循环情况 xff1a class B class A public B b class B public A a 若两个类之间存在循环依赖则在编译时会报错 xff0c 原因是两个类中存在相互的
  • Rust小项目一:Rust 网络编程,实现一个Tcp server

    近日学习Substrate的开发入门 xff0c 之前没有接触过Rust编程 xff0c 今天跟着视频做个小项目练练手 项目目标 xff1a 编写一个Tcp server端与一个Tcp client端 xff0c 客户端中输入内容后 xff
  • Python 项目打包并发布到私有 PyPI 服务器

    推广博客 xff1a Python 项目打包并发布到私有 PyPI 服务器
  • C++ 零碎特性

    摘自 C 43 43 17 入门经典 几乎不会再更新 文章目录 使用花括号初始化变量零初始化使大整型字面量更加易读二进制的整型字面量 96 size t 96 类型浮点数的特殊情况 xff1a NaN xff08 Not a Number
  • Python 学习笔记——进阶

    文章目录 一 模块 xff08 一 xff09 1 导入外部模块2 导入时重命名3 标准库 xff08 一 xff09 96 sys 96 96 argv 96 变量 96 exit 96 函数 96 modules 96 变量 96 pa
  • C++ UTF-8 编码与 UTF-32 编码的互相转换

    C 43 43 UTF 8 编码与 UTF 32 编码的互相转换 代码实现基本照搬了秦建辉的博客 这里不介绍原理 xff0c 只提供可以直接使用的代码 要求 C 43 43 编译器的语言标准至少为 C 43 43 17 如果编译器支持的语言
  • C++ 默认移动构造函数的调用情况

    C 43 43 默认移动构造函数的调用 直接上测试代码 xff1a include lt cstdio gt include lt iostream gt include lt string gt class MyClass int a 6
  • LaTeX 003:使用 MikTeX 组件实现 pdf 转 eps

    气死了气死了 xff0c 这玩意儿居然直接百度不到一个很好的答案 xff0c 百度到的全是用带图形化界面的软件 xff0c 您不累吗 xff1f 唯一找到的一个 xff0c 效果不好 xff0c 是糊的 最后还是上谷歌镜像站 xff0c 一
  • 【深入浅出ios开发】UIStoryboardSegue详解

    一个UIStoryboardSegue对象负责执行两个试图控制器之间的视觉过渡 另外 xff0c segue对象通常用来准备从一个控制器过渡到另一个控制器 segue对象包含了涉及过渡的控制器的信息 当segue被触发 xff0c 并且在视
  • C++ 多线程编程导论(上)

    随着摩尔定律逼近失效和多核处理器快速发展 xff0c 多线程编程变得越来越重要 本文将系统介绍在 C 43 43 中如何使用 STL 实现多线程编程 多线程编程博大精深 xff0c 本文并不介绍多线程算法或多线程编程方法 xff0c 而是把

随机推荐