树状数组--csu 1933

1933: 购买饮料


小明家门口有一个卖饮料的自动售货机,小明经常在那里买饮料。
每种饮料有一个流行度,表示它在市场上流行的程度,为了显示自己的与众不同,小明从来不买流行度大于t的饮料。
售货机上有三种操作:
1 在货架上添加一种流行度为x的饮料,并以j作为该饮料的编号,j表示该饮料是第j个被添加的饮料(j从1开始计数)。
2 编号为x的饮料已售罄。
3 小明尝试买第x流行的饮料。

Input

第一行一个整数t。
接下来若干行每行两个整数i,x。1<=i<=3 表示该操作是第i种操作。
输入以只包含一个整数0的一行结束。
0<t,x<=106

Output

对每个i=3的操作:
如果小明尝试买的饮料流行度v>t,输出"Sorry.";
如果小明尝试买的饮料流行度v<=t,并且未售罄,输出该饮料的流行度;
如果小明尝试购买的饮料已卖完,输出"Meiyou.".
每个输出占一行。

Sample Input

99
1 100
3 1
1 99
1 99
1 99
2 4
2 2
3 2
0

Sample Output

Sorry.
99

树状数组加二分的模板,首先,树状数组C[i]存储的是A[i-2^k+1]至A[i]的和,通过树状数组我们可以在log(n)时间内轻松求数组的前缀和和子段和,考虑到这种情况,数组的前缀和一定是非递减的,所以可以使用二分,我们只要将拥有的流行度的饮料建成一个树状数组通过求前缀和来确定前面有多少种类饮料,二分时候,假设我们要求第i流行的饮料,最后二分到第一个sum(前缀和)等于第i流行的饮料即可,二分区间为【left,right)左闭右开,所以二分时right=mid。

#include <iostream>
#include <stdio.h>
#include <string.h>
#define siz 1000005
#include <queue>
using namespace std;
int arr[siz],t,i,inde,SSum;
int vis[siz],C[siz],A[siz];
int lowbit(int x){
	return x&-x;
}


void Insert(int index,int x){
        A[index]=x;vis[x]++;
        if(vis[x]<=1){
           // cout<<x<<" !!!!!!!!!!!!!!!!!"<<endl;
            while(x<siz){
            C[x]+=1;
            x+=lowbit(x);
        }
        ++SSum;
        }

}
int Sum(int index){
    int sum=0;
    while(index>0){
        sum+=C[index];
        index-=lowbit(index);
    }
    return sum;
}
int erfen(int zx){
    int left=1,right=siz;
    int mid;
    while(left<right){

        mid=(left+right)>>1;
   // cout<<left<<" "<<right<<" "<<mid<<" "<<Sum(mid)<<" "<<zx<<endl;
        if(Sum(mid)<zx) left=mid+1;
        else if(Sum(mid)>=zx) right=mid;

    }
    //cout<<Sum(mid)<<"&&"<<mid<<endl;
    return left;
}
void New(int x){
    int d=A[x];
    vis[d]--;

}
void query(int x){
    //cout<<SSum<<"%%%%%%%%%%%%%%%%"<<endl;
    int zx=SSum-x+1;
    int ans=erfen(zx);
    //cout<<ans<<endl;
    if(ans>t){
       // cout<<"Sorry."<<endl;
       printf("Sorry.\n");
    }
    else{
        if(vis[ans]<=0) //cout<<"Meiyou."<<endl;
            printf("Meiyou.\n");
        else
        printf("%d\n",ans);
        //cout<<ans<<endl;
    }
}
int main()
{
    scanf("%d",&t);
    inde=0;
    int x;
    //right=1;
    while(1){
        scanf("%d",&i);
        if(!i) break;
        scanf("%d",&x);

        if(i==1){
            Insert(++inde,x);
            //++index;
        }
        else if(i==2){
            New(x);
        }
        else{
            query(x);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值