Splay树 + 离散化 —— HDU 3436 Queue-jumpers

对应HDU题目:点击打开链接

Queue-jumpers

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2754    Accepted Submission(s): 711


Problem Description
Ponyo and Garfield are waiting outside the box-office for their favorite movie. Because queuing is so boring, that they want to play a game to kill the time. The game is called “Queue-jumpers”. Suppose that there are N people numbered from 1 to N stand in a line initially. Each time you should simulate one of the following operations:
1.  Top x :Take person x to the front of the queue
2.  Query x: calculate the current position of person x
3.  Rank x: calculate the current person at position x
Where x is in [1, N].
Ponyo is so clever that she plays the game very well while Garfield has no idea. Garfield is now turning to you for help.
 

Input
In the first line there is an integer T, indicates the number of test cases.(T<=50)
In each case, the first line contains two integers N(1<=N<=10^8), Q(1<=Q<=10^5). Then there are Q lines, each line contain an operation as said above. 
 

Output
For each test case, output “Case d:“ at first line where d is the case number counted from one, then for each “Query x” operation ,output the current position of person x at a line, for each “Rank x” operation, output the current person at position x at a line.
 

Sample Input
  
  
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
 

Sample Output
  
  
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;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值