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

传送门


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

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

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

你可能感兴趣的:(Codeforces)