对应HDU题目:点击打开链接
3 9 5 Top 1 Rank 3 Top 7 Rank 6 Rank 8 6 2 Top 4 Top 5 7 4 Top 5 Top 2 Query 1 Rank 6
Case 1: 3 5 8 Case 2: Case 3: 3 6
有三种操作:Top x(将x提到第一位), Query x(询问元素x在第几位), Rank k (询问第k位的元素是谁)。
思路:
可以用伸展树实现。n有10^8那么大,而q只有10^5,所以应该离散化把Top和Query的操作数作为点,把他们之间的区间也作为点(区间内部的点不需要移动,所以可以这样),这样最多有200000左右的结点。
Top X :把X伸展到根,删掉X,在树的左下角添加结点X。
Query X :把X伸展到根,则sz[Left[T]] + 1就是结果
Rand X :从根开始根据情况往左右两边找
伸展树只需要保存子树的所有点(加上区间的所有点),以a[]数组的下标作为结点编号,对于Top和Query操作,可以通过二分查找确定结点编号。
C++ AC,G++ RE代码:
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define N 200024
#define nil (0x7fffffff)
using namespace std;
int Left[N];
int Right[N];
int fa[N];
int sz[N]; //以i为根的子树的结点数
int b[N>>1];
char Q[N>>1][7];
int X[N>>1];
typedef struct
{
int first, second;
}PAIR;
PAIR a[N]; //映射数组
int len;
//初始化
void Build(int pre, int &T, int x, int y)
{
int mid = ((x + y)>>1);
T = mid;
fa[T] = pre;
Left[T] = Right[T] = nil;
sz[T] = a[T].second;
if(x == y) return;
if(x < mid){
Build(T, Left[T], x, mid - 1);
sz[T] += sz[Left[T]];
}
if(mid < y){
Build(T, Right[T], mid + 1, y);
sz[T] += sz[Right[T]];
}
}
void R_rotate(const int x)
{
const int y = fa[x];
const int z = fa[y];
const int k = Right[x];
int sx = sz[x], sy = sz[y], sk = 0;
if(nil != k) sk = sz[k];
Left[y] = k;
Right[x] = y;
if(nil != z){
if(y == Left[z]) Left[z] = x;
else Right[z] = x;
}
if(nil != k) fa[k] = y;
fa[y] = x;
fa[x] = z;
sz[y] = sy - sx + sk;
sz[x] = sx - sk + sz[y];
}
void L_rotate(const int x)
{
const int y = fa[x];
const int z = fa[y];
const int k = Left[x];
int sx = sz[x], sy = sz[y], sk = 0;
if(nil != k) sk = sz[k];
Right[y] = k;
Left[x] = y;
if(nil != z){
if(y == Right[z]) Right[z] = x;
else Left[z] = x;
}
if(nil != k) fa[k] = y;
fa[y] = x;
fa[x] = z;
sz[y] = sy - sx + sk;
sz[x] = sx - sk + sz[y];
}
int Find(int x)
{
int l = 1, r = len, mid;
while(l <= r)
{
mid = ((l + r)>>1);
if(a[mid].first == x) return mid;
else if(a[mid].first < x) l = mid + 1;
else r = mid - 1;
}
return -1;
}
void Splay(int x, int &T)
{
if(nil == T) return;
int p, end;
end = fa[T];
p = x;
while(end != fa[x])
{
p = fa[x];
if(end == fa[p]){ //p是根结点
if(x == Left[p]) R_rotate(x);
else L_rotate(x);
break;
}
//p不是根结点
if(x == Left[p]){
if(p == Left[fa[p]]){
R_rotate(p); //LL
R_rotate(x); //LL
}
else{
R_rotate(x); //RL
L_rotate(x);
}
}
else{
if(p == Right[fa[p]]){ //RR
L_rotate(p);
L_rotate(x);
}
else{ //LR
L_rotate(x);
R_rotate(x);
}
}
}
T = x;
}
//在队首添加结点
void Add_at_lower_left_corner(int x, int T)
{
int p = T;
sz[p]++;
while(nil != Left[p]){
p = Left[p];
sz[p]++;
}
Left[p] = x;
sz[x] = 1;
fa[x] = p;
Left[x] = Right[x] = nil;
}
void Delete_root(int &T)
{
if(nil == T) return;
int l, r, x;
l = Left[T];
r = Right[T];
if(nil == l && nil == r){
T = nil;
return;
}
if(nil != l) fa[l] = nil;
if(nil != r) fa[r] = nil;
if(nil == l){ //没有左儿子
T = r;
return;
}
if(nil == r){ //没有右儿子
T = l;
return;
}
x = l;
while(nil != Right[x])
x = Right[x]; //x为T的左子树的中序最大值结点(右下角结点)
Splay(x, l); //把x伸展到左子树的根结点
T = l; //把左子树的根作为整棵树的根,此时T没有右子树
//接上右子树
Right[T] = r;
if(nil != r){
fa[r] = T;
sz[T] += sz[r];//更新结点数
}
}
void Top(int x, int &T)
{
x = Find(x);
Splay(x, T); //把x结点伸展到根
if(nil == Left[T]) return; //新根没有左儿子,根即为第一个数
Delete_root(T); //删除根结点
Add_at_lower_left_corner(x, T); //在T的左下角增加结点x
Splay(x, T);
}
int Rand(int x, int T)
{
if(nil == T) return 0;
int sum, p;
p = T;
sum = (nil == Left[p] ? 0 : sz[Left[p]]) + a[p].second;
while(sum != x)
{
if(sum > x){
if(x < sz[Left[p]] + 1) p = Left[p];
else return a[p].first - (sum - x);
}
else{
x -= sum;
p = Right[p];
}
if(nil == p) return 0;
sum = (nil == Left[p] ? 0 : sz[Left[p]]) + a[p].second;
}
return a[p].first;
}
int Query(int x, int &T)
{
x = Find(x);
if(nil == T) return 0;
Splay(x, T);
return (nil == Left[x] ? 0 : sz[Left[x]]) + 1;
}
//离散化
void Discrete(int n, int j)
{
int i, k;
sort(b + 1, b + j + 1);
k = 1;
for(i = 2; i <= j; i++) //去重
if(b[i] != b[k]) b[++k] = b[i];
len = 0;
for(i = 1; i <= k; i++){
if(b[i] - b[i-1] > 1){
a[++len].first = b[i] - 1;
a[len].second = b[i] - b[i-1] - 1;
}
a[++len].first = b[i];
a[len].second = 1;
}
if(b[k] < n){
a[++len].first = n;
a[len].second = n - b[k];
}
}
int main()
{
//freopen("in.txt","r",stdin);
int T, t, n, q, w = 0;
int i, j;
scanf("%d", &t);
while(t--)
{
printf("Case %d:\n", ++w);
scanf("%d%d", &n, &q);
j = 0;
for(i = 0; i < q; i++){
scanf("%s%d", Q[i], &X[i]);
if('T' == Q[i][0] || 'Q' == Q[i][0])
b[++j] = X[i];
}
Discrete(n, j); //离散化
Build(nil, T, 1, len);
for(i = 0; i < q; i++){
if('T' == Q[i][0]) Top(X[i], T);
else if('Q' == Q[i][0]) printf("%d\n", Query(X[i], T));
else printf("%d\n", Rand(X[i], T));
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
Splay树 + 离散化 —— HDU 3436 Queue-jumpers
原文:http://blog.csdn.net/u013351484/article/details/46876883