天天看点

Splay树 + 离散化 —— HDU 3436 Queue-jumpers 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左右的结点。

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

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

继续阅读