BZOJ 3339 Rmq Problem

【题目地址】点击打开链接


【题目大意】

给出长度为N的数字序列A[],Q个提问,每次询问为l,r,输出区间[l,r]的mex。

mex{S}定义为自然数(包括0)中最小的,不在集合S中的值。

N,Q,A[]范围均为20w级别

(尼玛,题目名字跟意思毫无关系啊有木有)


【分析】

我们可以预处理区间[1,x]的mex,用数组Mex[x]记录。 并用一棵线段树来保存区间信息,域即为mex值。

我们从左往右扫描A[]数组,扫描到位置i时,令j为值A[i]下一次出现的位置。

那么我们删除位置i的值的影响时,区间[i+1,j-1]之间的mex都要更新为,mex=min(mex,A[i])。

但如果直接更新,时间无法承受,注意到mex函数是单调不降的,所以我们可以二分求到mex值大于A[i]的位置。直接在线段树中修改区间。

删除位置i的值的影响后,所有以i+1为区间左端点的询问都是最新的。

可以直接用右端点从线段树的相应位置获取答案了。


【代码】

/*************************
    ID:Ciocio
    LANG:C++
    DATE:2014-1-29
    TASK:Mex
*************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>

using namespace std;

#define MAXN 200000
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rrep(i,b,a) for(int i=b;i>=a;--i)
#define Mid ((Left+Right)>>1)
#define lc (id<<1)
#define rc ((id<<1)|1)

int N,Q,p,q,ad;
int A[MAXN+10],Mex[MAXN+10];
int Last[MAXN+10],Next[MAXN+10];
struct wjj{
    int l,r;
    int rank,ans;
}Que[MAXN+10];
struct SegTree{
    int Key[(MAXN+10)<<2];

    void init(){
        memset(Key,-1,sizeof Key);
    }

    void putdown(int id){
        Key[lc]=
        Key[rc]=Key[id];
        Key[id]=-1;
    }

    void insert(int id,int Left,int Right){
        if(p<=Left&&Right<=q){
           Key[id]=ad;
           return;
        }
        if(Key[id]!=-1) putdown(id);
        if(!(q<Left||Mid<p)) insert(lc,Left,Mid);
        if(!(q<Mid+1||Right<p)) insert(rc,Mid+1,Right);
    }

    int find(int id,int Left,int Right){
        if(Key[id]!=-1) return Key[id];
        if(p<=Mid) return find(lc,Left,Mid);
        else return find(rc,Mid+1,Right);
    }
}Tree;
bool cmp1(const wjj& A,const wjj& B){
    return (A.l<B.l)||(A.l==B.l&&A.r<B.r);
}
bool cmp2(const wjj& A,const wjj& B){
    return A.rank<B.rank;
}

void _read(int& x){
    char tt=getchar();
    while(tt<'0'||'9'<tt) tt=getchar();
    for(x=0;'0'<=tt&&tt<='9';x=(x<<1)+(x<<3)+tt-'0',tt=getchar());
}

void _init(){
    _read(N);_read(Q);
    rep(i,1,N) _read(A[i]);
    rep(i,1,Q){
        _read(Que[i].l);
        _read(Que[i].r);
        Que[i].rank=i;
    }
    sort(Que+1,Que+Q+1,cmp1);
}

void _change(int loc){
    int l=loc+1,r=Next[loc]-1;
    int ans=-1;
    q=
    r=r>0?r:N;
    while(l<=r){
        int m=(l+r)>>1;
        p=m;
        if(Tree.find(1,0,MAXN+1)>=A[loc]){
            ans=m;
            r=m-1;
        }
        else
            l=m+1;
    }
    p=ans;
    if(ans==-1||Tree.find(1,0,MAXN+1)<=A[loc]) return;
    ad=A[loc];
    Tree.insert(1,0,MAXN+1);
}

void _solve(){
    //get Mex
    set <int> Set;
    rep(i,0,MAXN+1) Set.insert(i);
    rep(i,1,N){
        Set.erase(A[i]);
        Mex[i]=*Set.begin();
    }
    //build segtree
    Tree.init();
    Mex[0]=-1;
    rep(i,1,N){
        if(Mex[i]!=Mex[i-1]){
            int j=i;
            while(Mex[j+1]==Mex[j]&&j+1<=N) j++;
            p=i;q=j;ad=Mex[i];
            Tree.insert(1,0,MAXN+1);
            i=j;
        }
    }
    //get relationship of A's key
    rrep(i,N,1){
        Next[i]=Last[A[i]];
        Last[A[i]]=i;
    }
    //deal with the query
    int now=1;
    rep(i,1,Q){
        while(now!=Que[i].l) _change(now++);
        p=Que[i].r;
        Que[i].ans=Tree.find(1,0,MAXN+1);
    }
    //print
    sort(Que+1,Que+Q+1,cmp2);
    rep(i,1,Q)
        printf("%d\n",Que[i].ans);
}

int main(){
    _init();
    _solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值