hdu 3436 Queue-jumpers 树状数组

Queue-jumpers

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


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
题意:给你1--N的数字,从小到大排列,可以执行操作:
TOP x把x放到最前面
Rank x 问x数字在第几位
Query x wen在x位置上的数字是什么
这道题有很多解法:splay等高级数据结构可以写的,但是代码太多。树状数组+离散化比较容易些,但是很难想到
我用的是树状数组,但是比较麻烦
思路:分两步来做:
1.rank,query操作的x被调到队列前面了
初始tail = 100000,tail表示队列首位的位置,因为可能top100000次
setmap 记录x是否被top过,前当前的tail标号
hahamap 记录每个tail值对应的数字
每当top x时,如果x已经被top了 ,数组数组setmap[x]的值减一,就把setmap[x]=tail,同时更新hahamap[tail]=x,然后tail--。
如果没被top过,就直接更新setmap,hahamap,
rank x;如果x的大小<=setmap的大小,说明可以在setmap里找到这个数字,用二分的方法找到y,使得setmap[y]-tail+sum(y)==x就找到了rank 为x的数了, 这个树状数组表示前y个数中有几个值被重复top
query x:如果x被top过setmap[x] - tail + sum(y) 就是他的rank
2rank,query的x都不曾被top过
用一个map top ,top[x]表示x被top了几次,树状数组清空,top里存被top过的数有几个,且被top过几次
用一个vector low对所有的被top的数字从小到大排序,离线逆序解决问题,如果遇到top x,那么top[x]--,如果top[x]=0,表示在这个
top操作前,x没被top过,x在low所在的位置,position,在树状数组中position+1位置-1,表示当前位置少了一个数字。
为什么是+1位置呢:因为树状数组的add,sum操作,不能获得0位置上的值,
rank x:二分查找,对一个数y,top.size() + y - 被top过小于y的数字个数,就是
    
    
y的rank,如果==x,说明就是y了
query x操作:x + top.size() - low中存在的且<x的数字个数就是答案
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<map>
#include<vector>
#include<string>
#define sec second
#define fir first
using namespace std;
const int maxn = 100009;
int N,M;
int shu[maxn+1];
int lowbit(int i){
    return i&(-i);
}
int add(int i,int a){
    while(i <= maxn){
        shu[i] += a;
        i += lowbit(i);
    }
    return 0;
}
int sum(int i){
    int res = 0;
    while(i > 0){
        res += shu[i];
        i -= lowbit(i);
    }
    return res;
}
int tail = 0;
int res[maxn];
map<int,int> hahamap;
map<int,int> setmap;
vector<int>  low;
map<int,int> top;
pair<string,int> question[maxn];
int main()
{
    int t,x,position;char a[100];
    scanf("%d",&t);
    for(int tt = 1; tt <= t; tt++){
        printf("Case %d:\n",tt);
        scanf("%d%d",&N,&M);
        memset(shu,0,sizeof(shu));//树状数组清空
        memset(res,-1,sizeof(res));//答案标记
        low.clear();//排序数组清空
        hahamap.clear();//
        setmap.clear();
        top.clear();
        tail = 100000;
        for(int i = 0;i < M; i++){
            scanf("%s %d",a,&x);
            question[i].fir = (string)a;
            question[i].sec = x;
            if(a[0] == 'T'){
                hahamap[tail] = x;
                if(setmap.find(x) == setmap.end()){//没被top过,
                    low.push_back(x);//记录这个被top的值
                    top[x] = 1;
                    setmap[x] = tail--;
                }
                else{
                    top[x]++;
                    add(setmap[x],-1);
                    setmap[x] = tail--;
                }
            }
            else if(a[0] == 'Q'){
                if(setmap.find(x) != setmap.end()){
                    int ans = setmap[x] - tail + sum(setmap[x]);
                    res[i] = ans;
                }
            }
            else {
               if(setmap.size() >= x){
                   int li = tail,high = 100000,mid;//二分rank
                   while(li <= high){
                        mid = (li+high)/2;
                        int ans = mid - tail + sum(mid);//计算数字为mid的rank
                        if(ans < x) li = mid+1;//rank大于x,说明数字太小
                        else high = mid - 1;
                   }
                   res[i] = hahamap[li];
               }
            }
        }
        sort(low.begin(),low.end());//对被top的数排序
        memset(shu,0,sizeof(shu));//清空数组
        for(int i = M-1;i >= 0;i--){
            if(res[i] != -1) continue;
             x = question[i].sec;
            if(question[i].fir[0] == 'T'){
                top[x]--;//逆序中,检查x是否top次数为0了
                if(top[x] == 0){
                    top.erase(x);
                    position = lower_bound(low.begin(),low.end(),x) - low.begin();
                    add(position+1,-1);//在这次top前,top次数为0,表示这个x离开top序列了
                }
            }
            else if(question[i].fir[0] == 'R'){
                int li = x - top.size(), high = x ,mid;
                while(li <= high){//查找rank 为x的数字,
                    mid = (li+high)/2;
                    position = upper_bound(low.begin(),low.end(),mid) - low.begin();
                    int ans = top.size() + mid - (position + sum(position));//
                    //因为插入时,将x所在的位置+1了,而upper_bound找第一个大于x的数字
                    //所以不需要position-1+1的操作了
                    if(ans < x)li = mid+1;
                    else high = mid-1;
                }
                res[i] = li;
            }
            else{
                position = upper_bound(low.begin(),low.end(),x) - low.begin();
                int ans = top.size() + x - (position + sum(position));
                res[i] = ans;
            }
        }
        for(int i = 0;i < M; i++){
            if(question[i].fir[0] =='T')continue;
            printf("%d\n",res[i]);
        }
    }
    return 0;
}
/*
23
10000 18
T 3
T 4
T 7
Q 4
R 2
T 4
Q 4
Q 6
Q 7
Q 8
R 6
R 5
T 3
T 7
Q 4
Q 5
R 4
R 5
*/









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值