题目地址:
http://acm.hdu.edu.cn/showproblem.php?pid=3436
题意:
一个序列有 N(1<=N<=1e8) 个人,起初编号为1的人在序列的第1个位置,2在第2个位置……对这个序列有 Q(1<=Q<=1e5) 次操作,操作类型如下:
- Top x:把编号为x的人放到序列的最前面
- Query x:询问编号为x的人目前在序列的位置
- Rank x:询问目前在序列第x个位置的人的编号
思路:
比较基础的splay吧,就是数据范围过大,所以要先离散化,Top操作的方法是把x这个人删去,放到splay的最前面,Query则是用数组查找x这个人对应节点的编号,再得到该节点的在splay的位置,Rank只需要找到splay的第x个节点,输出相应值就好了。
可见Top,Query操作的x都是需要被单独拿出来的,其他的数字一段一段表示也没有问题,离散化时就依Top和Query中的x把数轴分段。
虽然splay是排序二叉树,但里面存的数值不需要是真正的有序,splay的操作使其始终符合排序二叉树的性质,然后我们可以利用这些性质完成一些模拟。
这是自己做的第一道splay,所以代码不是和我模板 里的代码完全一样。
额外注意一下,top()
函数如果不在最后一步把新节点翻转至根的话,就会T(有一组数据就是不断进行Top操作,很容易让没有这个操作的代码TLE)
由此见维护splay树的形状是一件很重要的事
还有删节点要把这个节点先旋转到根节点再删的原因,因为在根节点删除的话,就只需要pushup(root)
一次,如果不是在根节点的话,就要不断pushup更新整棵splay树,代价和把节点旋转到根节点差不多,代码却比只删根的策略来的复杂
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define MS(x, y) memset(x, y, sizeof(x))
#define lson l, m - 1
#define rson m + 1, r
const int MAXN = 1e5 + 5;
const int MAXM = MAXN << 1;
int s[MAXM], e[MAXM], cnt;
int node[MAXM];
int root, tot;
int siz[MAXM], data[MAXM], pre[MAXM], son[MAXM][2], num[MAXM];
char str[10];
int op[MAXN], par[MAXN];
int show_num[MAXN];
int n, q;
inline void init() {
tot = 1;
// son[root][0] = son[root][1] =
// pre[root] = siz[root] =
// num[root] = data[root] = 0;
}
int newnode(int k, int fa) {
pre[tot] = fa;
siz[tot] = e[k] - s[k] + 1;
num[tot] = siz[tot];
data[tot] = k;
node[k] = tot;
MS(son[tot], 0);
return tot++;
}
inline void pushup(int x) {
siz[x] = siz[son[x][0]] + siz[son[x][1]] + num[x];
}
int build(int fa, int l ,int r) {
if (l > r) return 0;
int m = (l + r) >> 1;
int x = newnode(m, fa);
son[x][0] = build(x, lson);
son[x][1] = build(x, rson);
pushup(x);
return x;
}
void Rotate(int x, int kind) {
int y = pre[x];
son[y][!kind] = son[x][kind];
pre[son[x][kind]] = y;
if (pre[y]) {
son[pre[y]][son[pre[y]][1] == y] = x;
}
pre[x] = pre[y];
son[x][kind] = y;
pre[y] = x;
pushup(y);
}
void splay(int x, int goal) {
while (pre[x] != goal) {
if (pre[pre[x]] == goal) Rotate(x, son[pre[x]][0] == x);
else {
int y = pre[x];
int kind = (son[pre[y]][0] == y);
if (son[y][kind] == x) {
Rotate(x, !kind);
Rotate(x, kind);
} else {
Rotate(y, kind);
Rotate(x, kind);
}
}
}
pushup(x);
if (goal == 0) root = x;
}
int Find(int val) {
int l = 0, r = cnt - 1, mid;
while (l <= r) {
mid = (l + r) >> 1;
if (s[mid] <= val && val <= e[mid]) return mid;
if (e[mid] < val) l = mid + 1;
else r = mid - 1;
}
}
int get_min(int x) {
while (son[x][0]) {
x = son[x][0];
}
return x;
}
void pushfront(int &r, int k, int fa) {
if (r == 0) {
r = newnode(k, fa);
return ;
}
pushfront(son[r][0], k, r);
pushup(r);
}
void deleteroot() {
if (!son[root][0] || !son[root][1]) {
root = son[root][0] + son[root][1];
pre[root] = 0;
return ;
}
int k = get_min(son[root][1]);
splay(k, root);
son[k][0] = son[root][0];
pre[son[root][0]] = k;
root = k;
pre[root] = 0;
pushup(root);
}
void top(int x) {
int k = Find(x);
int y = node[k];
splay(y, 0);
deleteroot();
pushfront(root, k, 0);
splay(tot - 1, 0);
}
int get_rank(int val) {
int k = Find(val);
int y = node[k];
splay(y, 0);
return siz[son[root][0]] + 1;
}
int get_kth(int k, int x = root) {
int t = siz[son[x][0]];
if (k <= t) return get_kth(k, son[x][0]);
if (k <= t + num[x]) return s[data[x]] + k - t - 1;
return get_kth(k - t - num[x], son[x][1]);
}
int main() { //freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);
int T, kase = 0;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &q);
int tol = 0;
show_num[tol++] = 0;
for (int i = 0; i < q; ++i) {
scanf("%s%d", str, par + i);
if (str[0] == 'T') op[i] = 1;
else if (str[0] == 'Q') op[i] = 2;
else op[i] = 3;
if (op[i] < 3) show_num[tol++] = par[i];
}
show_num[tol++] = n;
sort(show_num, show_num + tol);
cnt = 0;
for (int i = 1; i < tol; ++i) if (show_num[i] != show_num[i - 1]) {
if (show_num[i] - show_num[i - 1] > 1) {
s[cnt] = show_num[i - 1] + 1;
e[cnt] = show_num[i] - 1;
++cnt;
}
s[cnt] = show_num[i];
e[cnt] = show_num[i];
++cnt;
}
init();
root = build(0, 0, cnt - 1);
printf("Case %d:\n", ++kase);
for (int i = 0; i < q; ++i) {
if (op[i] == 1) top(par[i]);
else if (op[i] == 2) cout<<get_rank(par[i])<<endl;
else cout<<get_kth(par[i])<<endl;
}
}
}