【题目来源】
https://www.luogu.com.cn/problem/P3850
【题目描述】
Knuth 先生家里有个精致的书架,书架上有 N 本书,如今他想学到更多的知识,于是又买来了 M 本不同的新书。现在他要把新买的书依次插入到书架中,他已经把每本书要插入的位置标记好了,并且相应的将它们放好。由于 Knuth 年龄已大,过几天他已经记不清某些位置上放的到底是什么书了,请问你能帮助他吗?
【输入格式】
输入文件的第一行为整数 N,接下来 N 行分别是书架上依次放着的 N 本书的书名(书名由不含空格的字符串构成,长度不超过 10)。下一行将要输入一个整数 M,接下来的 M 行分别为这本书的书名和要插入的位置。下一行将要输入一个整数 Q,接下来共有 Q 次询问,每行都是一个整数表示询问的位置。(书架上位置的编号从 0 开始)
【输出格式】
输出 Q 行,每行对应着相应查询位置的书名。
【输入样例】
3
Math
Algorithm
Program
2
Picture 2
System 1
3
0
1
3
【输出样例】
Math
System
Picture
【说明/提示】
原来有三本书 Math、Algorithm、Program,后来又买了两本书,分别插入到 2 和 1 的位置,每次插入时其他书都要向后挪一个位置,最后书架上书的序列为:
0 Math
1 System
2 Algorithm
3 Picture
4 Program
Q 次询问依次为 0, 1, 3 位置的书,所以答案为:Math、System、Picture
对于 30% 的数据,1⩽N⩽100,1⩽M⩽10^3,1⩽Q⩽10^3
对于 100% 的数据,1⩽N⩽200,1⩽M⩽10^5,1⩽Q⩽10^4
对于 100% 的数据都符合题目中所描述的限制关系,数据保证每次插入的位置均不超过当时书架上书的数量,而且保证 Q 次查询中的每个位置上一定有书。
【算法分析】
● FHQ−Treap,也称非旋 Treap,由范浩强提出。顾名思义,FHQ−Treap 就是不需要通过旋转,而是通过分裂(split)与合并(merge)维护的 Treap。FHQ−Treap 与 Treap 的另外一个区别是 FHQ−Treap 可持久化。
● FHQ-Treap 的高明之处在于所有的操作都只用到了分裂(split)与合并(merge)这两个基本操作。
● 本例代码中的 maxn 依据题设数据范围设为 10^6。
【算法代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6;
struct node {
int ls,rs; //int ch[2];
int pri,val;
int size;
} tr[maxn];
char s[maxn][12];
int n,m;
int q;
int tot,root;
int newNode(int x) {
tot++;
tr[tot].size=1;
tr[tot].pri=rand();
tr[tot].val=x;
tr[tot].ls=tr[tot].rs=0;
return tot;
}
void update(int cur) {
tr[cur].size=tr[tr[cur].ls].size+tr[tr[cur].rs].size+1;
}
int merge(int x,int y) {
if(x*y==0) return x+y;
if(tr[x].pri<tr[y].pri) {
tr[x].rs=merge(tr[x].rs, y);
update(x);
return x;
} else {
tr[y].ls=merge(x,tr[y].ls);
update(y);
return y;
}
}
void split(int cur,int k,int &x,int &y) {
if(!cur)x=y=0;
else {
if(k>tr[tr[cur].ls].size) {
x=cur;
split(tr[cur].rs, k-tr[tr[cur].ls].size-1, tr[cur].rs,y);
} else {
y=cur;
split(tr[cur].ls, k, x, tr[cur].ls);
}
update(cur);
}
}
int kth(int cur,int k) {
int t=cur;
while(t) {
if(tr[tr[t].ls].size+1==k)return tr[t].val;
else if(tr[tr[t].ls].size>=k)t=tr[t].ls;
else {
k-=tr[tr[t].ls].size+1;
t=tr[t].rs;
}
}
}
void insert(int cur,int p) {
int x,y;
split(root,p-1,x,y);
root=merge(merge(x,newNode(cur)), y);
}
void output(int p) {
printf("%s\n",s[kth(root,p)]);
}
int main() {
//srand((unsigned)time(0));
scanf("%d",&n);
for(int i=1; i<=n; ++i) {
scanf("%s",s[i]);
root=merge(root,newNode(i));
}
scanf("%d",&m);
int p;
for(int i=n+1; i<=n+m; ++i) {
scanf("%s %d",s[i],&p);
insert(i,p+1);
}
scanf("%d",&q);
for(int i=0; i<q; ++i) {
scanf("%d",&p);
output(p+1);
}
return 0;
}
/*
in:
3
Math
Algorithm
Program
2
Picture 2
System 1
3
0
1
3
out:
Math
System
Picture
*/
【参考文献】
https://www.cnblogs.com/xixike/p/15151206.html
https://www.cnblogs.com/gryzy/p/10617570.html
https://blog.csdn.net/hnjzsyjyj/article/details/138504578
https://blog.csdn.net/hnjzsyjyj/article/details/120380473
https://www.acwing.com/solution/content/185366/