继续刷splay。orz
第一次遇到splay的离散化,留个纪念。传送门:Queue-jumpers
题目大意:
有 108 的人,一开始按照1,2,3…n排序,有q次操作。
一开始的编号为1,2,3…n,操作后编号不变。操作Top x,将编号为x的人放到最前面。
操作Rank x,告诉他编号为x的人排在哪里
操作Query x,告诉他第x位的人的编号是多少。
思路:
其实去掉 108 ,人数少一点,就是一个基本的splay裸题。
但是现在有 108 的人数,但是询问次数只有 105 。
这让我们很容易可以想到,把询问离线之后离散化。
但是只离散化询问的点是相对麻烦的。
因为你做了Top操作之后,他的子树的size一定为1。
但是我们在做new node的时候,对于最初的插入,size是需要通过计算离散化区间大小来得到的。
这样我么那就会写出两种new node 不是很好维护其实我们不妨在离散化点的时候,将他前一个点和后一个点,也顺带丢进去离散化里。
这些点是不会给操作到的,他们只是用来辅助算size的。
一个点的初始size为e[v] - s[v] + 1,这个e[v]和s[v]分别表示他的左端点和右端点。
那么他们要询问的点的size一开始一定是1,因为他的左端点和右端点都是自己。
而其我们做的就是辅助区间。二分的时候要自己手动实现一下
另外get_kth这个函数也需要改一改。其实操作麻烦的就只是Top而已。
我们可以先把他删除了,然后再将树上最小的点旋转到根,那么根的左子树一定是空的。然后对左子树进行插入就可以了
/*
@resources: hdu 3436
@date: 2017-09-04
@author: QuanQqqqq
@algorithm: splay
*/
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define ll long long
#define MAXN 100005
using namespace std;
int n;
struct Question {
char str[10];
int x;
} qsn[MAXN];
int pt[MAXN], ptlen; //离散化后的数组和长度
int s[MAXN], e[MAXN]; //这里储存离散化后的点,顺带把离散化后区间的点也储存,那些点只用来记size,不会被操作到
int cttn;
int binary_search(int x) {
int l = 0, r= cttn - 1, mid;
while (l <= r) {
mid = r + l >> 1;
if (s[mid] <= x && e[mid] >= x) {
return mid;
}
if (e[mid] < x) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
struct SplayTree {
const static int MAX_SIZE = 3e5 + 10;
const static int INF = 0x7ffffff;
int tree[MAX_SIZE][2], father[MAX_SIZE]; //该点的左右儿子,该点的爸爸节点,
int size[MAX_SIZE]; //该点的子节点数
int val[MAX_SIZE]; //每个点的价值
int node[MAX_SIZE]; //记录节点的位置
int root, sz; //该树的根,该树的节点数
int cnt; //中序遍历指针
int num[MAX_SIZE];
void Treaval(int x) {
if (x) {
Treaval(tree[x][0]);
printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d\n", x, tree[x][0], tree[x][1], father[x], size[x]);
Treaval(tree[x][1]);
}
}
void debug() {
printf("%d\n",root);
Treaval(root);
}
//更新节点
void push_up(int r) {
size[r] = size[tree[r][0]] + size[tree[r][1]] + num[r];
}
void new_node(int &r, int fa, int v) {
r = ++sz;
father[r] = fa;
val[r] = v;
node[v] = r;
size[r] = num[r] = e[v] - s[v] + 1;
tree[r][0] = tree[r][1] = 0;
}
void build(int &rt, int l, int r, int fa) { //建一棵满的二叉平衡树
if (l > r) {
return ;
}
int mid = l + r >> 1;
new_node(rt, fa, mid);
build(tree[rt][0], l, mid - 1, rt);
build(tree[rt][1], mid + 1, r, rt);
push_up(rt);
}
void clear() {
sz = root = cnt = 0;
val[0] = num[root] = size[root] = tree[0][0] = tree[0][1] = father[0] = 0;
build(root, 0, cttn - 1, root); //建一棵在n+1左边的完全二叉平衡树
}
void rotate(int x, int k) { //x是节点,k判断是左旋还是右旋
int y = father[x];
tree[y][!k] = tree[x][k]; //先变两边的儿子
father[tree[x][k]] = y; //再将x的儿子的爸爸变成y
if (father[y]) { //如果不是root,将y爸爸的儿子也变一下
tree[father[y]][tree[father[y]][1] == y] = x;
}
father[x] = father[y]; //更新x的父亲成y的父亲
father[y] = x; //更新y的父亲成x
tree[x][k] = y; //更新x的儿子成y
push_up(y);
}
void splay(int x, int r) { //将x旋转到r的儿子,这里注意是儿子!
if (r == 0) {
root = x;
}
while (father[x] != r) {
if (father[father[x]] == r) { //zig-step
rotate(x, tree[father[x]][0] == x);
return ;
}
int y = father[x];
int k = tree[father[y]][0] == y; //判断上上层是否是左边
if (tree[y][k] == x) { // zig-zag-step
rotate(x, !k);
rotate(x, k);
} else { // zig-zig-step
rotate(y, k);
rotate(x, k);
}
}
push_up(x);
}
int get_min(int r) {
push_up(r);
while (tree[r][0]) {
r = tree[r][0];
push_up(r);
}
return r;
}
int get_next(int x) { //获得比x下标大一点的下标
int rg = tree[x][1];
if (rg == 0) { //这里可能出现,他爸爸也是没东西或者,他爸爸是他前一个的情况,那你就要反思一下你的splay是不是建错了
return father[x];
}
while (tree[rg][0]) {
rg = tree[rg][0];
}
return rg;
}
int get_pre(int x) { //获得比x下标小一点的下标
int lf = tree[x][0];
//这里可能出现lf为0
while (tree[lf][0]) {
lf = tree[lf][0];
}
return lf;
}
int get_kth(int r, int k) { //获得第k大的root
int siz = size[tree[r][0]];
if (k <= siz) {
return get_kth(tree[r][0], k);
} else if (k <= siz + num[r]) { //这里要写小于等于号,因为里面有存在区间
return s[val[r]] + (k - siz) - 1;
} else {
return get_kth(tree[r][1], k - siz - num[r]);
}
}
void del() { //这里删除被旋转到root的那个
if (!tree[root][0] || !tree[root][1]) {
root = tree[root][0] + tree[root][1];
father[root] = 0;
return ;
}
int k = get_min(tree[root][1]); //找到最小的那个
splay(k, root);
tree[tree[root][1]][0] = tree[root][0];
root = tree[root][1];
father[tree[root][0]] = root;
father[root] = 0;
push_up(root);
}
void insert(int &r, int k, int fa) { //这里插入到最前的那个位置
if (r == 0) {
new_node(r, fa, k);
return ;
}
insert(tree[r][0], k, r);
push_up(r);
}
void top_point(int a) {
int y = node[a];
splay(y, 0);
del();
insert(root, a, 0);
splay(sz, 0); //听说这步不加会TLE
}
int get_rank(int x) {
x = node[x];
splay(x, 0);
return size[tree[root][0]] + 1;
}
};
SplayTree spl;
int q;
void solve() {
for (int i = 0; i < q; i++) {
int idx = binary_search(qsn[i].x);
if (qsn[i].str[0] == 'T') {
spl.top_point(idx);
} else if (qsn[i].str[0] == 'R') {
printf("%d\n", spl.get_kth(spl.root, qsn[i].x));
} else {
printf("%d\n", spl.get_rank(idx));
}
}
}
int main() {
int T, cas = 1;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &q);
int len = 0;
pt[len++] = 0;
for (int i = 0; i < q; i++) {
getchar();
scanf("%s %d", qsn[i].str, &qsn[i].x);
if (qsn[i].str[0] == 'Q' || qsn[i].str[0] == 'T') {
pt[len++] = qsn[i].x;
}
}
pt[len++] = n;
cttn = 0;
sort(pt, pt + len);
for (int i = 1; i < len; i++) { //离散化
if (pt[i] != pt[i - 1]) {
if (pt[i] - pt[i - 1] > 1) {
s[cttn] = pt[i - 1] + 1;
e[cttn] = pt[i] - 1;
cttn++;
}
s[cttn] = pt[i];
e[cttn] = pt[i];
cttn++;
}
}
spl.clear();
printf("Case %d:\n", cas++);
solve();
}
}