Educational Codeforces Round 87 (Rated for Div. 2) D. Multiset(树状数组+二分)

传送门


1.题目大意:给出一个序列和 q q q次操作,每次操作要么序列中添加 k ( 1 ≤ k ≤ n ) k(1 \leq k \leq n) k(1kn),要么就找到数组中的第 k k k小的元素删除,最后如果序列存在元素则输出任意一个,否则输出 0 0 0

2.一看到第 k k k小,想到了快排和归并,但是二者的时间复杂度都是 O ( n ) O(n) O(n)的,对于 q q q次询问,时间复杂度达到了 O ( n 2 ) O(n^2) O(n2),肯定不行的。看数据,应该需要我们在 l o g n logn logn的时间内添加和删除元素

3.最重要的一点,因为 k k k的取值范围不超过 n n n,这就好处理多了,我们考虑树状数组,然后对每个数出现的次数建树,这样就能 l o g n logn logn地插入数字,并且统计次数。但是最后删除第 k k k小,需要从 1 1 1开始遍历直到前缀和等于 k k k吗?显然不需要,树状数组维护的前缀和是递增的,因此我们直接使用二分查找即可 l o g n logn logn时间内解决

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=1e6+10;

int t[maxn];
int n,q,k,x;

void update(int i,int k){
    while(i<=maxn){
        t[i]+=k;
        i+=lowbit(i);
    }
}

ll ask(int i){
    ll ans=0;
    for(;i;i-=lowbit(i)){
        ans+=t[i];
    }
    return ans;
}

int getAns(){
    for(int i=1;i<=n;i++)
        if(t[i]) return i;
    return 0;
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&q);
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        update(x,1);
    }
    while(q--){
        scanf("%d",&k);
        if(k>0){
            update(k,1);
        }else{
            k=-k;
            int l=1,r=n+10;
            while(l<r){
                int mid=(l+r)/2;
                if(ask(mid)<k) l=mid+1;
                else r=mid;
            }
            update(l,-1);
        }
    }
    printf("%d\n",getAns());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值