[JZOJ5710] Mex

Description

给出长度为N的序列A
M次询问,每次询问一个区间的Mex值

M e x ( S ) 表示在集合S中未出现过的最小自然数
强制在线
N , M 200000

Solution

显然值大于n的数都是没用的

一个很自然的思路是主席树

如果我们直接用主席树维护每种数的出现次数,那我们在查找的时候是无法确定往哪边走的,因为要做减法再统计哪些位置为0

那我们可以直接维护下标
还是主席树,每个数记录它在这个位置之前最后一次出现的下标
一个区间就维护这些下标的最小值

最后要查询的时候,判断是否所有左儿子区间中的数都在查询区间中,是的话就要往右儿子走,否则就往左儿子走

时空复杂度 O ( N log N )

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define M 15000005
using namespace std;
int rt[N],t1,n,a[N],mi[M],t[M][2],n1,m;
void build(int k,int x,int l,int r,int w,int v)
{
    if(l==r) mi[k]=v;
    else
    {
        int mid=(l+r)>>1;
        if(w<=mid) t[k][0]=++n1,t[k][1]=t[x][1],build(t[k][0],t[x][0],l,mid,w,v);
        else t[k][0]=t[x][0],t[k][1]=++n1,build(t[k][1],t[x][1],mid+1,r,w,v);
        mi[k]=min(mi[t[k][0]],mi[t[k][1]]);
    }
}
int find(int k,int l,int r,int x)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(mi[t[k][0]]<x) return find(t[k][0],l,mid,x);
    else return find(t[k][1],mid+1,r,x);
}
int main()
{
    cin>>n>>m>>t1;
    int mx=0;
    fo(i,1,n) 
    {
        scanf("%d",&a[i]);
        rt[i]=++n1;
        if(a[i]<=n+1) build(rt[i],rt[i-1],0,n+1,a[i],i);
        else n1--,rt[i]=rt[i-1];
    }
    int ans=0;
    fo(i,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(t1) x^=ans,y^=ans;
        ans=find(rt[y],0,n+1,x);
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/80300030
Mex
今日推荐