P2596 [ZJOI2006]书架
原来splay还能换钱呐!
这道题让我明白了一点:原来splay还可以不是二叉排序树啊!
本来第一次想,就认为我们应该要维护一个优先级,然后每个操作就更改一下。
但其实只要用来维护这段序列就可以了。。。
看一下这五种操作:
-
将某元素置顶。先splay到根,如果左子树为空就无须操作,否则寻找后继,合成到后继的左子树,同时删除根的左子树。
-
将某元素置底。先splay到根,如果右子树为空就无须操作,否则寻找前驱,合成到前驱的右子树,同时删除根的右子树。
-
将某元素前移、不动、后移一位。只需要找到元素后交换val就可以了。
-
询问某元素前面有多少个元素。splay到根后输出左子树的size就可以了。
-
询问第k个元素的编号。平衡树第k大操作。
注意一下插入操作,我们有这么一串原始序列,我们只要每一次都插入到最右的叶子结点就可以保证我们的这个数插入到最后。
虽然看上去是\(O(n^2)\)的,但是只要每一次插入后都splay一下,就能保证树的平衡,就优化为\(O(nlogn)\)了。
因为不按任何值进行排序,所以我们需要记录某个值的元素的下标。我们用一个pos数组维护。
在1和2操作中,其实没必要用到split思想,不然你就需要两个虚拟结点,实现起来稍微麻烦一点。
代码:
#include<cstdio>
#include<algorithm>
const int maxn = 80005, INF = 19260817;
struct Splay
{
int fa, ch[2], size, val;
} s[maxn];
int root, tot;
int pos[maxn];
int n, m;
int identify(int x)
{
return s[s[x].fa].ch[1] == x;
}
void connect(int son, int fa, int k)
{
s[son].fa = fa;
s[fa].ch[k] = son;
}
void pushup(int x)
{
s[x].size = s[s[x].ch[0]].size + s[s[x].ch[1]].size + 1;
pos[s[s[x].ch[0]].val] = s[x].ch[0];
pos[s[s[x].ch[1]].val] = s[x].ch[1];
}
void rotate(int x)
{
int y = s[x].fa;
int z = s[y].fa;
int yk = identify(x);
int zk = identify(y);
int b = s[x].ch[yk ^ 1];
connect(b, y, yk);
connect(y, x, yk ^ 1);
connect(x, z, zk);
pushup(y);
pushup(x);
}
void splay(int x, int goal)
{
while(s[x].fa != goal)
{
int y = s[x].fa;
int z = s[y].fa;
if(z != goal) identify(x) == identify(y) ? rotate(y) : rotate(x);
rotate(x);
}
//
if(goal == 0) root = x;
}
void insert(int x)
{
int now = root;
while(s[now].ch[1]) now = s[now].ch[1];
now = ++tot;
s[now].fa = now - 1; s[now - 1].ch[1] = now;
s[now].size = 1; s[now].ch[0] = s[now].ch[1] = 0;
s[now].val = x;
pos[x] = now;
splay(now, 0);
}
void top(int x)// x is val
{
splay(pos[x], 0);
if(s[root].ch[0] == 0) return;
if(s[root].ch[1] == 0)
{
s[root].ch[1] = s[root].ch[0];
s[root].ch[0] = 0;
}
else
{
int now = s[root].ch[1];
while(s[now].ch[0]) now = s[now].ch[0];
connect(s[root].ch[0], now, 0);
s[root].ch[0] = 0;
splay(s[now].ch[0], 0);
}
}
void bottom(int x)
{
splay(pos[x], 0);
if(s[root].ch[1] == 0) return;
if(s[root].ch[0] == 0)
{
s[root].ch[0] = s[root].ch[1];
s[root].ch[1] = 0;
}
else
{
int now = s[root].ch[0];
while(s[now].ch[1]) now = s[now].ch[1];
connect(s[root].ch[1], now, 1);
s[root].ch[1] = 0;
splay(s[now].ch[1], 0);
}
}
void Insert(int x, int y)
{
if(y == 0) return;
splay(pos[x], 0);
int del;
if(y == 1)
{
del = s[root].ch[1];
while(s[del].ch[0]) del = s[del].ch[0];
}
else if(y == -1)
{
del = s[root].ch[0];
while(s[del].ch[1]) del = s[del].ch[1];
}
std::swap(pos[s[del].val], pos[s[root].val]);
std::swap(s[del].val, s[root].val);
}
int ask(int x)
{
splay(pos[x], 0);
return s[s[root].ch[0]].size;
}
int query(int k)
{
int now = root;
while(2333)
{
int left = s[now].ch[0];
if(s[left].size + 1 < k)
{
k -= s[left].size + 1;
now = s[now].ch[1];
}
else if(s[left].size >= k) now = left;
else return now;
}
}
void print(int u)
{
if(s[u].ch[0]) print(s[u].ch[0]);
printf("%d ", s[u].val);
if(s[u].ch[1]) print(s[u].ch[1]);
}
int read()
{
int ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
{
if(ch == '-') s = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return s * ans;
}
int main()
{
n = read(), m = read();
//insert(0);
for(int i = 1; i <= n; i++)
{
int temp = read();
insert(temp);
}
//insert(INF);
//for(int i = 1; i <= n; i++) printf("%d\n", pos[i]);
while(m--)
{
char opt[20];
scanf("%s", opt); int x = read();
if(opt[0] == 'T')
{
top(x);
}
else if(opt[0] == 'B')
{
bottom(x);
}
else if(opt[0] == 'I')
{
int y = read();
Insert(x, y);
}
else if(opt[0] == 'A')
{
printf("%d\n", ask(x));
}
else if(opt[0] == 'Q')
{
printf("%d\n", s[query(x)].val);
}
//print(root); printf("\n");
}
return 0;
}