HDU 3436 Queue-jumpers(splay tree)

题意:一开始n个人排除一队, 从1~n, 有三种操作:TOP 把第x个人换到最前面;Query输出第x个人的位置;Rank输出第x个位置的是哪个人。

思路:由于N非常大, 很自然想到先离散化,这题的难点主要就在这里了, 离散化处理得好, 下面就好办了 。 这题要把Top 和Query离散化(这两个的x都表示人), 然后记录区间最左边的人是谁, 记录最右边的人是谁, 然后要改下pushup函数不再是+1 而是 +这个区间的人数

对于Top操作, 先把第x个人移动到根节点, 然后再根据他左右孩子的情况把他移动到最前面, 这题有一组很恶心的样例, 最后还要把这个人移动到根结点, 不然会超时

对于Query操作, 直接把第x个人移动到根节点, 左子树结点的总和+1就是他现在的位置

对于Ran操作,直接从根结点往下找

OK附上代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200015;

int root, ch[N][2], fa[N], sz[N];
int num[N], lsh[N], il[N], ir[N], sum[N];
char op[N][6];

inline void pushup(int x)
{
    sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + sum[x];
}

void rotate(int x, bool f)
{
    int y = fa[x];
    int z = fa[y];
    ch[y][!f] = ch[x][f];
    fa[ch[x][f]] = y;
    fa[x] = z;
    if(z)
        ch[z][ch[z][1] == y] = x;
    ch[x][f] = y;
    fa[y] = x;
    pushup(y);
}

void splay(int x, int g)
{
    int y = fa[x];
    while(y != g)
    {
        int z = fa[y];
        bool f = (ch[y][0] == x);
        if(z != g && f == (ch[z][0] == y))
            rotate(y, f);
        rotate(x, f);
        y = fa[x];
    }
    pushup(x);
    if(g == 0)
        root = x;
}

int qcf(int n)
{
    int ans = 2;
    for(int i = 2; i < n; i++)
        if(lsh[i] != lsh[i - 1])
            lsh[ans++] = lsh[i];
    return ans;
}

void build(int l, int r, int f)
{
    if(l > r)
        return ;
    if(l == r)
    {
        ch[l][0] = ch[l][1] = 0;
        sz[l] = sum[l];
        fa[l] = f;
        return ;
    }
    int m = (l + r) >> 1;
    fa[m] = f;
    ch[m][0] = (m - 1 >= l)? ((l + m - 1) >> 1): 0;
    ch[m][1] = (r >= m + 1)? ((m + 1 + r) >> 1): 0;
    build(l, m - 1, m);
    build(m + 1, r, m);
    pushup(m);
}

int bfind(int l, int r, int a)
{
    while(l <= r)
    {
        int m = (l + r) >> 1;
        if(il[m] <= a && ir[m] >= a)
            return m;
        if(ir[m] < a)
            l = m + 1;
        else
            r = m - 1;
    }
    return -1;
}

void tomin(int x, int r)
{
    while(ch[r][0])
    {
        sz[r] += sz[x];
        r = ch[r][0];
    }
    sz[r] += sz[x];
    ch[r][0] = x;
    fa[x] = r;
}

void top(int id)
{
    splay(id, 0);
    int ls = ch[root][0];
    if(ls == 0)	//这个人已经在最前面了
        return ;
    if(ch[root][1] == 0) //没有右结点只能把左节点当做根节点
    {
        root = ls;
        fa[root] = 0;
        ch[id][0] = ch[id][1] = 0;
        sz[id] = sum[id];
        tomin(id, ls); //把这个人移动到最前面
        return ;
    }
    root = ch[root][1];
    fa[root] = 0;
    ch[id][0] = ch[id][1] = 0;
    sz[id] = sum[id];
    tomin(id, ls);	//把这个人移动到最前面
    tomin(ls, root); //把左节点移动到根节点
    splay(id, 0);//这句话相当重要, 不加就会超时= 0 =, 这题有一组恶心的数据
}

int que(int id)
{
    splay(id, 0);
    return sz[ch[root][0]] + 1;
}

int ran(int x)
{
    x--;
    int r = root;
    while(x != sz[ch[r][0]])
    {
        if(x < sz[ch[r][0]])
            r = ch[r][0];
        else
        {
            x -= sz[ch[r][0]] + sum[r];
            if(x < 0)
                return il[r] + sum[r] + x;
            r = ch[r][1];
        }
    }
    return il[r];
}

int main()
{
    int T, C = 1;
    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        scanf("%d%d", &n , &m);
        int cnt = 1;
        for(int i = 0; i < m; i++)
        {
            scanf("%s%d", op[i], num + i);
            if(op[i][0] != 'R')
                lsh[cnt++] = num[i];
        }
        printf("Case %d:\n", C++);
        if(cnt > 1)
        {
            sort(lsh + 1, lsh + cnt);
            cnt = qcf(cnt); //去重复
            int ans = 1;
            if(lsh[1] == 1)
            {
                il[1] = 1;
                ir[1] = 1;
                sum[1] = 1;
            }
            else
            {
                il[1] = 1;
                ir[1] = lsh[1] - 1;
                sum[1] = lsh[1] - 1;
                il[2] = ir[2] = lsh[1];
                sum[2] = 1;
                ans = 2;
            }
            for(int i = 2; i < cnt; i++)
            {
                if(lsh[i] - lsh[i - 1] > 1)
                {
                    il[++ans] = lsh[i - 1] + 1;
                    ir[ans] = lsh[i] - 1;
                    sum[ans] = ir[ans] - il[ans] + 1;
                }
                il[++ans] = lsh[i];
                ir[ans] = lsh[i];
                sum[ans] = 1;
            }
            if(ir[ans] != n)
            {
                ir[++ans] = n;
                il[ans] = ir[ans - 1] + 1;
                sum[ans] = ir[ans] - il[ans] + 1;
            }
            fa[0] = sz[0] = ch[0][0] = ch[0][1] = 0;
            root = (1 + ans) >> 1;
            build(1, ans, 0);
            for(int i = 0; i < m; i++)
            {
                if(op[i][0] == 'T')
                {
                    int id = bfind(1, ans, num[i]);
                    top(id);
                }
                else if(op[i][0] == 'Q')
                {
                    int id = bfind(1, ans, num[i]);
                    printf("%d\n", que(id));
                }
                else
                {
                    printf("%d\n", ran(num[i]));
                }
            }
        }
        else
        {
            for(int i = 0; i < m; i++)
                printf("%d\n", num[i]);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值