rope 块状链表
题目一
P6166 [IOI2012]scrivener (https://www.luogu.com.cn/problem/P6166)
题目描述
有些人说李奥纳多是一个对于 Johannes Gutenberg 的崇拜者,Johannes 是一个发明活字印刷的德国铁匠,为了表达尊敬,李奥纳多设计了一台机器被称为小龙虾代书,那是一个非常简单的打字设备。这机器就像一部简单的现代打字机,但只能接受两个指令。一个指令是 输出一个字符,另一个指令是取消最近的指令。小龙虾代书的最大特点就是拥有这个功能强大的取消指令。因为一个取消指令本身也是一个指令,所以也可以被取消。
说明
你的任务是作出此小龙虾代书的程序,一开始并无输出任何文字,然后开始接受使用者输入的一连串指令,并可查询目前输出文字中的特定位置的字符。详细说明如下:
TypeLetter(L) —附加一个小写字母L在输出文字的最后,L 可以是 a,b,⋯,z。
UndoCommands(U) — 取消之前的 U 个指令,U 是一个正整数。
GetLetter(P) — 回传在输出文字中位置为 P 的字符,P 是一个非负整数。 输出文字中的第一个字符的位置为 0。 (这个查询并不是一个指令,因此会被取消指令忽略。)
三种操作可以依照任何顺序被呼叫 0 次或多次以上。
指令 UndoCommands(U) 会依照原本执行的相反顺序来取消前面 U 个指令: 如果被取消的指令是 TypeLetter(L),就会从输出文字最后面移除字母 L。如果被取消的指令是 UndoCommands(X),那么将会依照原本执行的顺序重新执行之前被取消的 X 个指令。
我们列出一连串可能的指令,以及每次执行指令后的输出文字。
操作 回传 输出文字
TypeLetter(a) a
TypeLetter(b) ab
GetLetter(1) b ab
TypeLetter(d) abd
UndoCommands(2) a
UndoCommands(1) abd
GetLetter(2) d abd
TypeLetter(e) abde
UndoCommands(1) abd
UndoCommands(5) ab
TypeLetter(c) abc
GetLetter(2) c abc
UndoCommands(2) abd
GetLetter(2) d abd
输入格式
第 11 行,一个正整数 NN,表示操作的个数。
第 22 到 N+1N+1 行,每行开始有一个大写字母参数。
T 表示一个 TypeLetter 指令,后面接着一个空白和一个小写字母参数。
U 表示一个 UndoCommands 指令,后面接着一个空白和一个整数参数。
P 表示一个 GetLetter 指令,后面接着一个空白和一个整数参数。
输出格式
对于每项 GetLetter 操作,输出一行,一个回传的字符。
输入
10
T c
T z
T u
T a
T i
T h
T f
T z
P 3
P 0
输出
a
c
思路
可持久化一下就可以了。数据加强,一个样例点会MLE
#include<bits/stdc++.h>
#include<ext/rope>
using namespace __gnu_cxx;
using namespace std;
const int maxn=1e6+10;
rope<char> *root[maxn];
int n;
int id[maxn], N=0;
int main() {
scanf("%d", &n);
root[0]=new rope<char>();
for(int i=1; i<=n; i++){
root[i]=new rope<char>(*root[i-1]);//可持久化
char k[5];
scanf("%s", k);
if(k[0]=='T'){
char x[5]; scanf("%s", x);
root[i]->push_back(x[0]);
id[++N]=i;
}
else if(k[0]=='U'){
int x; scanf("%d", &x);
int pos=id[N-x];
root[i]=root[pos];
id[++N]=i;
}
else{
int x; scanf("%d", &x);
printf("%c\n", root[i]->at(x));
}
}
return 0;
}
题目二
bzoj 3673 可持久化并查集 by zky(https://darkbzoj.tk/problem/3673)
Description
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
0<n,m<=2*10^4
Sample Input
5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
Sample Output
1
0
1
思路
#include<bits/stdc++.h>
#include<ext/rope>
using namespace __gnu_cxx;
using namespace std;
rope<int> *root[20005];
int a[20005];
int fd(int i, int x) {
if(root[i]->at(x)==x)
return x;
int fx=fd(i, root[i]->at(x));
root[i]->replace(x, fx);
return fx;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i=0; i<=n; i++) {
a[i]=i;
}
root[0]=new rope<int> (a, a+n+1);
for(int i=1; i<=m; i++) {
root[i]=new rope<int> (*root[i-1]);//可持久化
int op;
scanf("%d", &op);
if(op==1) {
int x, y;
scanf("%d%d", &x, &y);
x=fd(i, x), y=fd(i, y);
if(x!=y) {
root[i]->replace(x, y);
}
} else if(op==2) {
int x;
scanf("%d", &x);
root[i]=root[x];//会退版本
} else {
int x, y;
scanf("%d%d", &x, &y);
x=fd(i, x), y=fd(i, y);
printf("%d\n", x==y);
}
}
return 0;
}
题目三
P4008 [NOI2003]文本编辑器 (https://www.luogu.com.cn/problem/P4008)
题目描述
很久很久以前,DOS3.x 的程序员们开始对 EDLIN 感到厌倦。于是,人们开始纷纷改用自己写的文本编辑器⋯⋯
多年之后,出于偶然的机会,小明找到了当时的一个编辑软件。进行了一些简单的测试后,小明惊奇地发现:那个软件每秒能够进行上万次编辑操作(当然,你不能手工进行这样的测试) !于是,小明废寝忘食地想做一个同样的东西出来。你能帮助他吗?
为了明确目标,小明对“文本编辑器”做了一个抽象的定义:
文本:由 0 个或多个 ASCII 码在闭区间 [32, 126] 内的字符构成的序列。
光标:在一段文本中用于指示位置的标记,可以位于文本首部,文本尾部或文本的某两个字符之间。
文本编辑器:由一段文本和该文本中的一个光标组成的,支持如下操作的数据结构。如果这段文本为空,我们就说这个文本编辑器是空的。
操作名称 输入文件中的格式 功能
Move(k) Move k 将光标移动到第 k 个字符之后,如果 k=0,将光标移到文本开头
Insert(n,s) Insert n s 在光标处插入长度为 n 的字符串 s,光标位置不变n≥1
Delete(n) Delete n 删除光标后的 n 个字符,光标位置不变,n≥1
Get(n) Get n 输出光标后的 n 个字符,光标位置不变,n≥1
Prev() Prev 光标前移一个字符
Next() Next 光标后移一个字符
你的任务是:
建立一个空的文本编辑器。
从输入文件中读入一些操作并执行。
对所有执行过的 GET 操作,将指定的内容写入输出文件。
输入格式
输入文件 editor.in 的第一行是指令条数 t,以下是需要执行的 t 个操作。其中:
为了使输入文件便于阅读, Insert 操作的字符串中可能会插入一些回车符, 请忽略掉它们(如果难以理解这句话,可以参照样例) 。
除了回车符之外,输入文件的所有字符的 ASCII 码都在闭区间 [3232, 126126] 内。且
行尾没有空格。
这里我们有如下假定:
MOVE 操作不超过 50000 个, INSERT 和 DELETE 操作的总个数不超过 4000,PREV 和 NEXT 操作的总个数不超过 200000。
所有 INSERT 插入的字符数之和不超过 2M(1024M=1024×1024 字节) ,正确的输出文件长度不超过 3M 字节。
DELETE 操作和 GET 操作执行时光标后必然有足够的字符。 MOVE 、 PREV 、 NEXT 操作必然不会试图把光标移动到非法位置。
输入文件没有错误。
对 C++ 选手的提示:经测试,最大的测试数据使用 fstream 进行输入有可能会比使用 stdio 慢约 11 秒。
输出格式
输出文件 editor.out 的每行依次对应输入文件中每条 Get 指令的输出。
输入
15
Insert 26
abcdefghijklmnop
qrstuv wxy
Move 15
Delete 11
Move 5
Insert 1
^
Next
Insert 1
_
Next
Next
Insert 4
./.
Get 4
Prev
Insert 1
^
Move 0
Get 22
输出
./.
abcde_f./.ghijklmno
思路
很模板,Insert时注意'\n', '\r'
#include<bits/stdc++.h>
#include<ext/rope>
using namespace __gnu_cxx;
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
rope<char> rop;
char c[3000050];
int main() {
int t=read();
char op[10]; int x;
int pos=0;
while(t--){
scanf("%s", op);
if(op[0]=='I'){//插入长度为n的字符串
int n=read();
for(int i=0; i<n; i++){
c[i]=getchar();
while(c[i]=='\n'||c[i]=='\r'){
c[i]=getchar();
}
}
c[n]=0;
rop.insert(pos, c);
}
if(op[0]=='M'){
pos=read();
}
if(op[0]=='D'){//输出pos后面n个字符
int n=read();
rop.erase(pos, n);
}
if(op[0]=='P'){//输出pos后面n个字符
pos--;
}
if(op[0]=='N'){//输出pos后面n个字符
pos++;
}
if(op[0]=='G'){//输出光标后n个字符
int n=read();
rop.copy(pos,n,c);
c[n]=0;
printf("%s\n", c);
}
}
return 0;
}
题目四
题目描述
这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器。你能帮助他吗?为了明确任务目标,可可对“文本编辑器”做了一个抽象的定义:
Move k:将光标移动到第走个字符之后,如果 k=0,将光标移到文本第一个字符之前。
Insert n (换行) S:在光标后插入长度为 nn 的字符串 SS,光标位置不变,n≥1。
Delete n:删除光标后的n个字符,光标位置不变,n≥1。
Rotate n:反转光标后的n个字符,光标位置不变,n≥1。
Get:输出光标后的一个字符,光标位置不变。
Prev:光标前移一个字符。
Next:光标后移一个字符。
下面是几个定义:
文本:由 0 个或多个字符构成的序列。这些字符的 ASCII 码在闭区间 [32, 126] 内,也就是说,这些字符均为可见字符或空格。
光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。
文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下七条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。
编写一个程序:
建立一个空的文本编辑器。
从输入文件中读入一些操作指令并执行。
对所有执行过的 GET 操作,将指定的内容写入输出文件。
输入格式
输入文件中第一行是指令条数 N,以下是需要执行的 N 个操作。除了回车符之外,输入文件的所有字符的 ASCII 码都在闭区间 [32, 126] 内。且行尾没有空格。
输出格式
依次对应输入文件中每条 GET 指令的输出,不得有任何多余的字符。
输入
10
Insert 13
Balanced eert
Move 2
Delete 5
Next
Insert 7
editor
Move 0
Get
Move 11
Rotate 4
Get
输出
B
t
思路
翻转操作维护两个正反rope就可以了。
题目数据有锅。Insert 不能省'\n',Get输出一个如果是'\n',就不能再输出'\n'。其他就是常规操作
#include<bits/stdc++.h>
#include<ext/rope>
using namespace __gnu_cxx;
using namespace std;
char s[2000005];
rope<char> rp, fz, cs;
int main() {
int n; scanf("%d", &n);
int pos=0;
while(n--){
char op[10]; scanf("%s", op);
if(op[0]=='M'){
scanf("%d", &pos);
}
else if(op[0]=='I'){
int x; scanf("%d%*c", &x);
for(int i=0; i<x; i++){
s[i]=getchar();
}
s[x]=0;
rp.insert(pos, s, x);
for(int l=0, r=x-1; l<=r; l++, r--){
swap(s[l], s[r]);
}
fz.insert(fz.size()-pos, s, x);
}
else if(op[0]=='D'){
int x; scanf("%d", &x);
int Len=rp.size();
rp.erase(pos, x);
fz.erase(Len-pos-x, x);
}
else if(op[0]=='R'){
int x; scanf("%d",&x);
int Len=rp.size();
cs=rp.substr(pos,x);
rp=rp.substr(0, pos)+fz.substr(Len-pos-x, x)+rp.substr(pos+x, Len-pos-x);
fz=fz.substr(0, Len-pos-x)+cs+fz.substr(Len-pos, pos);
}
else if(op[0]=='G'){
//printf("%c\n", rp[pos]);
char c=rp[pos];
putchar(c),c^'\n'&&putchar('\n');//询问,注意特判回车
}
else if(op[0]=='P'){
pos--;
}
else if(op[0]=='N'){
pos++;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)