HDU 3436 Queue-jumpers splay

3 篇文章 0 订阅

题目链接:HDU 3436

Queue-jumpers 

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


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
 

题意:一个1到n的序列,有3种操作:把x提到序列首部,查询x的位置,查询序列的第x位

题目分析:由于n过大,所以需要离散化之后缩点,将Top和Query的数提取出来然后离散化插入平衡树

Top操作:把节点提根,找后继节点提到右儿子,删除根节点,然后splay。

Query操作:把节点提根,然后查询排名rank

Rank操作:同上,二分查找具体属于那个点,然后输出该点值+偏移。

//
//  main.cpp
//  HDU 3436 Queue-jumpers
//
//  Created by teddywang on 2017/8/30.
//  Copyright © 2017年 teddywang. All rights reserved.
//

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2e5+10;

int pre[maxn];// – 储存某点的父节点
int son[maxn][2];// – 储存某点的两个儿子
int siz[maxn];// – 以某点为根节点的子树大小
int num[maxn];// – 储存某点的值出现了几次
int data[maxn];// – 某点的值
int node[maxn];// – 某值的点
int rev[maxn];// – lazy数组,记录以某点为根的子树有没有被翻转
int add[maxn];// – lazy数组,记录以这个节点为根的子树有没有集体增加值的操作
int show_num[maxn],par[maxn],op[maxn];
int s[maxn],e[maxn];
int tot;// – 目前产生的点数
int root;// – 根节点
int n,q,cnt;
#define MS(x, y) memset(x, y, sizeof(x))
#define lson l, m - 1
#define rson m + 1, r
// 把l - 1移到根,把r + 1移到根的右节点,那么keyvalue指的就是[l, r]区间
#define keyvalue son[son[root][1]][0]

//void treaval(int x) {
//    if (!x) return ;
//    treaval(lson(x));
//    printf("结点%2d: 左儿子 %2d 右儿子 %2d 父节点 %2d size = %2d, val = %2d\n",
//           x, lson(x), rson(x), pre[x], siz[x], data[x]);
//    treaval(rson(x));
//}
//
//void debug() {
//    printf("root: %d\n", root);
//    treaval(root);
//}
inline void init() {
    tot = 1;
 }

int newnode(int k, int fa) {
    pre[tot] = fa;
    siz[tot] = e[k] - s[k] + 1;
    num[tot] = siz[tot];
    data[tot] = k;
    node[k] = tot;
    MS(son[tot], 0);
    return tot++;
}

inline void pushup(int x) {
    siz[x] = siz[son[x][0]] + siz[son[x][1]] + num[x];
}

int build(int fa, int l ,int r) {
    if (l > r) return 0;
    int m = (l + r) >> 1;
    int x = newnode(m, fa);
    son[x][0] = build(x, lson);
    son[x][1] = build(x, rson);
    pushup(x);
    return x;
}

void Rotate(int x, int kind) {
    int y = pre[x];
    son[y][!kind] = son[x][kind];
    pre[son[x][kind]] = y;
    if (pre[y]) {
        son[pre[y]][son[pre[y]][1] == y] = x;
    }
    pre[x] = pre[y];
    son[x][kind] = y;
    pre[y] = x;
    pushup(y);
}

void splay(int x, int goal) {
    while (pre[x] != goal) {
        if (pre[pre[x]] == goal) Rotate(x, son[pre[x]][0] == x);
        else {
            int y = pre[x];
            int kind = (son[pre[y]][0] == y);
            if (son[y][kind] == x) {
                Rotate(x, !kind);
                Rotate(x, kind);
            } else {
                Rotate(y, kind);
                Rotate(x, kind);
            }
        }
    }
    pushup(x);
    if (goal == 0) root = x;
}

int Find(int val) {
    int l = 0, r = cnt - 1, mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (s[mid] <= val && val <= e[mid]) return mid;
        if (e[mid] < val) l = mid + 1;
        else r = mid - 1;
    }
    return 0;
}

int get_min(int x) {
    while (son[x][0]) {
        x = son[x][0];
    }
    return x;
}

void pushfront(int &r, int k, int fa) {
    if (r == 0) {
        r = newnode(k, fa);
        return ;
    }
    pushfront(son[r][0], k, r);
    pushup(r);
}

void deleteroot() {
    if (!son[root][0] || !son[root][1]) {
        root = son[root][0] + son[root][1];
        pre[root] = 0;
        return ;
    }
    int k = get_min(son[root][1]);
    splay(k, root);
    son[k][0] = son[root][0];
    pre[son[root][0]] = k;
    root = k;
    pre[root] = 0;
    pushup(root);
}

void top(int x) {
    int k = Find(x);
    int y = node[k];
    splay(y, 0);
    deleteroot();
    pushfront(root, k, 0);
    splay(tot - 1, 0);
}

int get_rank(int val) {
    int k = Find(val);
    int y = node[k];
    splay(y, 0);
    return siz[son[root][0]] + 1;
}

int get_kth(int k, int x = root) {
    int t = siz[son[x][0]];
    if (k <= t) return get_kth(k, son[x][0]);
    if (k <= t + num[x]) return s[data[x]] + k - t - 1;
    return get_kth(k - t - num[x], son[x][1]);
}


int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        int tol=0;
        show_num[tol++]=0;
        char str[10];
        for(int i=0;i<q;i++)
        {
            scanf("%s%d",str,&par[i]);
            if(str[0]=='T') op[i]=1;
            else if(str[0]=='Q') op[i]=2;
            else op[i]=3;
            if(op[i]<3) show_num[tol++]=par[i];
        }
        show_num[tol++]=n;
        sort(show_num,show_num+tol);
        cnt=0;
        for(int i=1;i<tol;i++)
        {
            if(show_num[i]!=show_num[i-1])
            {
                if(show_num[i]-show_num[i-1]>1)
                {
                    s[cnt]=show_num[i-1]+1;
                    e[cnt++]=show_num[i]-1;
                }
                s[cnt]=show_num[i];
                e[cnt++]=show_num[i];
            }
        }
        init();
        root=build(0,0,cnt-1);
        printf("Case %d:\n",++cas);
        for(int i=0;i<q;i++)
        {
            if(op[i]==1) top(par[i]);
            else if(op[i]==2) printf("%d\n",get_rank(par[i]));
            else printf("%d\n",get_kth(par[i],root));
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值