BZOJ 3339: Rmq Problem【莫队+树状数组】

3339: Rmq Problem

【题目描述】
传送门

【题解】

一眼就是莫队题,但是答案有的难求,其实可以二分这个答案,然后check用树状数组。
树状数组求的是小于mid的这个数有几个,当然不算重复。
如果get(mid)==mid的话,那么表示从1到mid都出现过,当然,A数组要+1。
否则,说明在1到mid中有一个数没出现过。

代码如下

#pragma GCC optimize(2)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
#define MAXN 200005
using namespace std;
int n,q,K,tot,a[MAXN],f[MAXN],hsh[MAXN],Ans[MAXN];
struct xcw{
    int L,R,id;
    bool operator <(const xcw b)const{return (L/K<b.L/K)||(L/K==b.L/K)&&(R<b.R);}
}Q[MAXN];
int read(){
    int ret=0;bool f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) f^=!(ch^'-');
    for(; isdigit(ch);ch=getchar()) ret=(ret<<3)+(ret<<1)+ch-48;
    return f?ret:-ret;
}
void put(int x,int p){for(;x<=tot;x+=x&-x) f[x]+=p;}
int get(int x){
    int Sum=0;
    for(;x;x-=x&-x) Sum+=f[x];
    return Sum;
}
void Del(int x){
    hsh[a[x]]--;
    if(hsh[a[x]]==0) put(a[x],-1);
}
void Add(int x){
    if(hsh[a[x]]==0) put(a[x],1);
    hsh[a[x]]++;
}
int Fnd(){
    int Ret;
    if(tot==get(tot)) return tot+1;
    for(int L=1,R=tot,mid=(R+L)>>1;L<=R;mid=(R+L)>>1) if(mid==get(mid)) L=mid+1;else Ret=mid,R=mid-1;
    return Ret;
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("prob.in","r",stdin);
    freopen("prob.out","w",stdout);
    #endif
    n=read(),q=read();K=sqrt(n);
    for(int i=1;i<=n;i++) tot=max((a[i]=read()+1),tot);
    for(int i=1;i<=q;i++) Q[i]=(xcw){read(),read(),i};
    sort(Q+1,Q+1+q);
    int L=1,R=0;
    for(int i=1;i<=q;i++){
        while(L<Q[i].L&&L<=n) Del(L++);
        while(L>Q[i].L&&L>2) Add(--L);
        while(R<Q[i].R&&R<n) Add(++R);
        while(R>Q[i].R&&R>0) Del(R--);
        Ans[Q[i].id]=Fnd()-1;
    }
    for(int i=1;i<=q;i++) printf("%d\n",Ans[i]);
    return 0;
}
posted @ 2018-06-15 19:08  XSamsara  阅读(129)  评论(0编辑  收藏  举报