题目
题目描述
小
T
T
T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用
1
1
1到
n
n
n的正整数给每本书都编了号。 小
T
T
T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小
T
T
T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有
X
X
X本书,那么放回去时这本书上面就只可能有
X
−
1
X-1
X−1、
X
X
X或
X
+
1
X+1
X+1本书。 当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小
T
T
T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小
T
T
T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:
- 编号为 X X X的书在书柜的什么位置;
- 从上到下第 X X X本书的编号是多少。
答案对 N + 1 N+1 N+1取模。——好吧原题目没有这句话,我只是想把它改得面目全非而已。
输入输出格式
输入格式:
第一行有两个数
n
,
m
n,m
n,m,分别表示书的个数以及命令的条数。
第二行为 n n n个正整数:第 i i i个数表示初始时从上至下第 i i i个位置放置的书的编号。
第三行到 m + 2 m+2 m+2行,每行一条命令。命令有 5 5 5种形式:
- Top S \text{Top S} Top S——表示把编号为 S S S的书放在最上面。
- Bottom S \text{Bottom S} Bottom S——表示把编号为 S S S的书放在最下面。
- Insert S T \text{Insert S T} Insert S T—— T ∈ { − 1 , 0 , 1 } T\in\{-1,0,1\} T∈{−1,0,1},若编号为 S S S的书上面有 X X X本书,则这条命令表示把这本书放回去后它的上面有 X + T X+T X+T本书。
- Ask S \text{Ask S} Ask S——询问编号为 S S S的书的上面目前有多少本书。
- Query S \text{Query S} Query S——询问从上面数起的第 S S S本书的编号。
输出格式:
对于每一条
Ask
\text{Ask}
Ask或
Query
\text{Query}
Query语句你应该输出一行,一个数,代表询问的答案。
数据范围与约定
100
%
100\%
100%的数据,
n
,
m
≤
80000
n,m\le 80000
n,m≤80000
思路
考虑用平衡树解决这个问题。
我们的目标是找到一种 key \text{key} key值的定义,使得这样一条性质成立:如果书 x x x的 key \text{key} key小于书 y y y的 key \text{key} key,那么书架中 x x x在 y y y的上面。
更通俗一点,让平衡树的中序遍历恰好为书架中的书顺序。
发现这是可以做的。存储一个 L , R \mathcal{L,R} L,R表示当前已经使用的 key \text{key} key中的最小值和最大值。
放到顶端,相当于 key x = L − 1 \text{key}_x=\mathcal L-1 keyx=L−1;放到底部,相当于 key x = R + 1 \text{key}_x=\mathcal R+1 keyx=R+1。记得更新 L \mathcal L L和 R \mathcal R R。
与旁边的某本书交换位置,直接交换 key \text{key} key即可。
更改 key \text{key} key的时候,把原来的删掉,然后插入新的。
然后 Ask \text{Ask} Ask和 Query \text{Query} Query就是排名查询和查询第 k k k个元素。多板啊!
代码
我写的带旋
Treap
\text{Treap}
Treap。而且用了类模板。拷贝下来,就是整个世界
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -1;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar(x%10+'0');
}
template < typename T >
class Treap{
struct Node{
T data; int cnt, size, prio;
Node *son[2], *fa;
Node(const T &__data):data(__data){
prio = rand(), cnt = size = 1;
son[0] = son[1] = fa = NULL;
}
bool operator<(const Node &that)const{
return prio < that.prio;
}
void maintain(){
size = cnt;
if(son[0] != NULL)
size += son[0]->size;
if(son[1] != NULL)
size += son[1]->size;
}
};
Node* root;
void rotate(Node* &o,int d){
Node* k = o->son[d];
o->son[d] = k->son[d^1];
if(o->son[d] != NULL)
o->son[d]->fa = o;
k->son[d^1] = o;
k->fa = o->fa; o->fa = k;
o->maintain(), k->maintain();
o = k; // change root
}
void insert(T __data,Node* &o){
if(o == NULL){
o = new Node(__data);
return ;
}
if(o->data == __data){
++ o->cnt; ++ o->size;
return ;
}
int d = o->data < __data;
insert(__data,o->son[d]);
o->son[d]->fa = o;
if(*o < *(o->son[d]))
rotate(o,d);
o->maintain();
}
void erase(T __data,Node* &o){
if(o == NULL)
return ; // err to erase
int d;
if(o->data == __data){
if(o->cnt > 1){
-- o->cnt; -- o->size;
return ;
}
if(o->son[0] == NULL and o->son[1] == NULL){
delete o; o = NULL; return ;
}
if(o->son[0] == NULL or o->son[1] == NULL)
d = (o->son[1] != NULL);
else d = (*(o->son[0]) < *(o->son[1]));
rotate(o,d);
erase(__data,o->son[d^1]);
}
else{
d = o->data < __data;
erase(__data,o->son[d]);
}
o->maintain();
}
unsigned getRank(T __data,Node* o){
if(o == NULL)
return 0; // nothing more
unsigned lsize = (o->son[0] == NULL ? 0 : o->son[0]->size);
if(o->data == __data)
return 1+lsize;
int d = o->data < __data;
return d*(lsize+o->cnt)+getRank(__data,o->son[d]);
}
T kthElement(unsigned k,Node* o){
unsigned lsize = (o->son[0] == NULL ? 0 : o->son[0]->size);
if(k > lsize){
k -= lsize, k -= o->cnt;
if(k <= 0) return o->data;
return kthElement(k,o->son[1]);
}
return kthElement(k,o->son[0]);
}
void dispose(Node* &o){
if(o == NULL)
return ;
dispose(o->son[0]);
dispose(o->son[1]);
delete o; o = NULL;
}
public:
void dispose(){
dispose(root);
}
void clear(){
root = NULL;
}
Treap(){ clear(); }
void insert(T __data){
insert(__data,root);
}
void erase(T __data){
erase(__data,root);
}
unsigned getRank(T __data){
return getRank(__data,root);
}
T kthElement(unsigned k){
if(root != NULL and k){
unsigned all = root->size;
if(k > all) k = all;
return kthElement(k,root);
}
}
};
struct ZXY{
int val, id;
ZXY(int V,int I):val(V),id(I){}
bool operator<(const ZXY &that)const{
return val < that.val;
}
bool operator==(const ZXY &that)const{
return val == that.val;
}
};
const int MaxN = 80000;
int ppl[MaxN+1], n, m, lid, rid;
Treap<ZXY> meimei;
void input(){
srand(5201314);
n = readint(), m = readint();
lid = 1, rid = n;
for(int i=1; i<=n; ++i)
ppl[readint()] = i;
for(int i=1; i<=n; ++i)
meimei.insert(ZXY(ppl[i],i));
}
char cmd[1478];
void solve(){
int one, two;
while(m --){
scanf("%s",cmd);
one = readint();
if(cmd[0] == 'T'){
meimei.erase(ZXY(ppl[one],one));
ppl[one] = -- lid;
meimei.insert(ZXY(ppl[one],one));
}
if(cmd[0] == 'B'){
meimei.erase(ZXY(ppl[one],one));
ppl[one] = ++ rid;
meimei.insert(ZXY(ppl[one],one));
}
if(cmd[0] == 'I'){
two = readint();
if(not two) continue;
int located = meimei.getRank(ZXY(ppl[one],one));
two = meimei.kthElement(located+two).id;
meimei.erase(ZXY(ppl[one],one));
meimei.erase(ZXY(ppl[two],two));
swap(ppl[one],ppl[two]);
meimei.insert(ZXY(ppl[one],one));
meimei.insert(ZXY(ppl[two],two));
}
if(cmd[0] == 'A'){
writeint(meimei.getRank(ZXY(ppl[one],one))-1);
putchar('\n');
}
if(cmd[0] == 'Q'){
writeint(meimei.kthElement(one).id);
putchar('\n');
}
}
}
int main(){
input(), solve();
return 0;
}