这题用Splay写得我蛋疼菊紧,4000b的代码还有debug半天,看来我的splay姿势还不够好a = =
首先N是很大的,所以离散化是必然的,把要Top操作的和要Query操作的编号单独划分为一个区间,然后再中间会产生其他的区间,把这些区间缩点,然后离散化就好了。
三个操作其实不难实现,Top操作只要先把这个节点删除,然后把节点的其他值不变,key值变成一个极小值再重新插入就好,可以把极小值直接设成0,因为据说splay是稳定的,不过保险起见我还是设置了一个tpos变量维护了一下当前的极小值,还是比较方便的。
Rank操作其实就是一个找第k大,不同的就是这里节点本身的大小有可能不是1,要稍微处理一下。
Query操作就是找有几个节点比指定节点小,直接把这个节点splay到根,然后取左子树大小用即可。一开始我写成先找到这个节点,然后沿着父节点一路往上加的SB做法,直接TLE了两发,没有利用好splay的性质a,多伸展几下树也会平衡一点o(╯□╰)o
至于如何快速找到person,只要维护一个数组指向节点的编号就好。
不过感觉这题用线段树很好写a,只要在前面留Q个空位就好了。 。。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 3e5 + 10;
int ch[maxn][2], fa[maxn], rsize[maxn], size[maxn], key[maxn], nstr[maxn], chcnt, root;
int n, q, vnum[maxn], vcnt, qval[maxn], tpos, pos[maxn], pid[maxn];
int node_size[maxn], node_str[maxn], node_cnt;
char cmd[maxn][16];
inline void pushup(int x) {
size[x] = rsize[x];
for (int i = 0; i < 2; i++) if (ch[x][i]) {
size[x] += size[ch[x][i]];
}
}
int getID(int v) {
return lower_bound(vnum, vnum + vcnt, v) - vnum;
}
void build_node() {
//缩点
for (int i = 1; i < vcnt; i++) {
int tmp = vnum[i] - vnum[i - 1] - 1;
if (tmp) {
node_size[++node_cnt] = tmp;
node_str[node_cnt] = vnum[i - 1] + 1;
}
node_size[++node_cnt] = 1;
node_str[node_cnt] = vnum[i];
pid[i] = node_cnt;
}
if (vnum[vcnt - 1] < n) {
node_size[++node_cnt] = n - vnum[vcnt - 1];
node_str[node_cnt] = vnum[vcnt - 1] + 1;
}
}
int newNode(int &r, int father, int v, int rz, int ns) {
r = ++chcnt;
ch[r][0] = ch[r][1] = 0;
rsize[r] = rz;
key[r] = v;
fa[r] = father;
nstr[r] = ns;
return r;
}
void build_tree(int l, int r, int &rt, int father) {
int mid = l + r >> 1;
rt = pos[mid] = newNode(rt, father, mid, node_size[mid], node_str[mid]);
if (l < mid) build_tree(l, mid - 1, ch[rt][0], rt);
if (r > mid) build_tree(mid + 1, r, ch[rt][1], rt);
pushup(rt);
}
inline void rotate(int x, int d) {
int y = fa[x];
ch[y][d ^ 1] = ch[x][d];
fa[ch[x][d]] = y;
if (fa[y]) ch[fa[y]][y == ch[fa[y]][1]] = x;
fa[x] = fa[y];
ch[x][d] = y;
fa[y] = x;
pushup(x);
pushup(y);
}
inline void splay(int x, int goal) {
while (fa[x] != goal) {
int y = fa[x], d = (x == ch[y][1]);
if (fa[y] == goal) rotate(x, d ^ 1);
else {
int z = fa[y], d1 = (y == ch[z][1]);
if (d == d1) {
rotate(y, d ^ 1); rotate(x, d ^ 1);
}
else {
rotate(x, d ^ 1); rotate(x, d1 ^ 1);
}
}
}
pushup(x);
if (goal == 0) root = x;
}
inline int findMax(int rt) {
while (ch[rt][1]) rt = ch[rt][1];
return rt;
}
inline void remove(int x) {
splay(x, 0);
int lc = ch[x][0], rc = ch[x][1];
if (lc) {
int u = findMax(lc);
splay(u, x);
fa[u] = 0;
if(rc) fa[rc] = u;
ch[u][1] = rc;
root = u;
pushup(u);
}
else if(rc) {
fa[rc] = 0;
root = rc;
pushup(rc);
}
else root = 0;
}
inline int insert(int v, int rz, int ns) {
int u = root;
if (root == 0) {
newNode(root, 0, v, rz, ns);
return root;
}
while (ch[u][v > key[u]]) u = ch[u][v > key[u]];
int r = newNode(ch[u][v > key[u]], u, v, rz, ns);
splay(r, 0);
return r;
}
inline void setTop(int x) {
x = pid[getID(x)];
remove(pos[x]);
pos[x] = insert(--tpos, node_size[x], node_str[x]);
}
inline int query(int x) {
x = pos[pid[getID(x)]];
splay(x, 0);
return size[ch[x][0]] + 1;
}
inline int findkth(int rt, int k) {
int lsize = size[ch[rt][0]];
if (lsize >= k) return findkth(ch[rt][0], k);
else if (lsize + rsize[rt] >= k) return nstr[rt] + k - lsize - 1;
else return findkth(ch[rt][1], k - lsize - rsize[rt]);
}
int main() {
int T; scanf("%d", &T);
for (int kase = 1; kase <= T; kase++) {
printf("Case %d:\n", kase);
scanf("%d%d", &n, &q);
//加入把要进行Top,和Query操作的值离散化
vcnt = node_cnt = tpos = 0;
vnum[vcnt++] = 0;
chcnt = 0;
for (int i = 1; i <= q; i++) {
scanf("%s%d", cmd[i], &qval[i]);
if (cmd[i][0] == 'T' || cmd[i][0] == 'Q') {
vnum[vcnt++] = qval[i];
}
}
sort(vnum, vnum + vcnt);
vcnt = unique(vnum, vnum + vcnt) - vnum;
build_node();
build_tree(1, node_cnt, root, 0);
for (int i = 1; i <= q; i++) {
if (cmd[i][0] == 'T') setTop(qval[i]);
else if (cmd[i][0] == 'R') printf("%d\n", findkth(root, qval[i]));
else printf("%d\n", query(qval[i]));
}
}
return 0;
}