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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-29 19:50:39

Splay树 + 离散化 —— HDU 3436 Queue-jumpers的相关文章

【线段树】HDU 5493 Queue (2015 ACM/ICPC Asia Regional Hefei Online)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5493 题目大意: N个人,每个人有一个唯一的高度h,还有一个排名r,表示它前面或后面比它高的人的个数,求按身高字典序最小同时满足排名的身高排列. 题目思路: [线段树] 首先可以知道,一个人前面或后面有r个人比他高,那么他是第r+1高或第n-i-r+1高,i为这个人是第几高的. 所以先将人按照身高从小到大排序,接下来,把当前这个人放在第k=min(r+1,n-i-r+1)高的位置. 用线段树维护包

hdu 4441 Queue Sequence(splay)

题目链接:hdu 4441 Queue Sequence 这题看了题解写的,题解传送门 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 #define ls l,m,rt<<1 4 #define rs m+1,r,rt<<1|1 5 using namespace std; 6 typedef long long ll; 7 8 const int N=1e6+7; 9 i

HDU 1542 Atlantis 线段树+离散化+扫描线

题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define lson rt<<1,l,mid #define rson rt<<1|1,mid

HDU 3743 Frosh Week (线段树+离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3743 Frosh Week Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 5   Accepted Submission(s) : 1 Font: Times New Roman | Verdana | Georgia Font Size: 

HDU 3642 线段树+离散化+扫描线

题意:给你N个长方体的左下角和右上角坐标,问你空间中有多少体积是被大于两个不同的立方体覆盖的.x,y~10^6 z~500 考虑到给的z比较小,所以可以直接枚举z,然后跑二维的扫描线就好. 关于处理被不同的线段覆盖三次的问题,可以维护四个信息,cnt,once,twice,more,然后相互推出结果就好. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> #

HDU 1255 覆盖的面积 (扫描线 线段树 离散化)

题目链接 题意:中文题意. 分析:纯手敲,与上一道题目很相似,但是刚开始我以为只是把cnt>=0改成cnt>=2就行了,. 但是后来发现当当前加入的线段的范围之前 还有线段的时候就不行了,因为虽然现在都不等于 2,但是之前的那个线段加上现在的已经覆盖2次了. 1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cstring> 5 #include &

POJ 1177/HDU 1828 picture 线段树+离散化+扫描线 轮廓周长计算

求n个图矩形放下来,有的重合有些重合一部分有些没重合,求最后总的不规则图型的轮廓长度. 我的做法是对x进行一遍扫描线,再对y做一遍同样的扫描线,相加即可.因为最后的轮廓必定是由不重合的线段长度组成的,这样理论上是对的 要注意处理高度相同的线段,把底边优先处理(在代码里就是f标记为1的线段),因为若是一个矩形的底边和另一个矩形的上边重合,则这个轮廓肯定不能算 不过POJ和HDU的数据好像都比较弱,我没进行上面的细节处理也AC了,不过一个很简单的数据就会不对,所以还是要处理一下才是真正正确的代码 我

hdu 5124 lines (线段树+离散化)

lines Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 620    Accepted Submission(s): 288 Problem Description John has several lines. The lines are covered on the X axis. Let A is a point which

HDU5124:lines(线段树+离散化)或(离散化思想)

http://acm.hdu.edu.cn/showproblem.php?pid=5124 Problem Description John has several lines. The lines are covered on the X axis. Let A is a point which is covered by the most lines. John wants to know how many lines cover A. Input The first line conta