BZOJ3585&3339mex——主席树

题目描述

有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。

输入

第一行n,m。
第二行为n个数。
从第三行开始,每行一个询问l,r。

输出

一行一个数,表示每个询问的答案。

样例输入

5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5

样例输出

1
2
3
0
3

提示

数据规模和约定

对于100%的数据:

1<=n,m<=200000

0<=ai<=109

1<=l<=r<=n

对于30%的数据:

1<=n,m<=1000

  刚看到题可能有人会想建权值线段树然后维护区间出现数字次数,但有可能区间内出现的都是同一个数,无法判断。因为询问在l~r内最小没有出现过的数,所以可以建权值线段树,每个叶子节点记录这个权值最晚出现的时间,维护区间出现时间的最小值。对于询问l~r只需要在r时刻线段树中查询区间出现时间最小值小于l的就行了。

最后附上代码。

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mid (L+R)/2
using namespace std;
int n,m;
int x,y;
int cnt;
int a[200010];
int l[6000010];
int r[6000010];
int mx[6000010];
int root[200010];
int updata(int pre,int L,int R,int v,int c)
{
    int rt=++cnt;
    l[rt]=l[pre];
    r[rt]=r[pre];
    if(L==R)
    {
        mx[rt]=c;
    }
    else
    {
        if(v<=mid)
        {
            l[rt]=updata(l[pre],L,mid,v,c);
        }
        else
        {
            r[rt]=updata(r[pre],mid+1,R,v,c);
        }
        mx[rt]=min(mx[l[rt]],mx[r[rt]]);
    }
    return rt;
}
int query(int x,int L,int R,int v)
{
    if(L>=R)
    {
        return L;
    }
    if(v>mx[l[x]])
    {
        return query(l[x],L,mid,v);
    }
    else
    {
        return query(r[x],mid+1,R,v);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        root[i]=updata(root[i-1],0,1e8,a[i],i);
    }
    while(m--)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",query(root[y],0,1e8,x));
    }
}

猜你喜欢

转载自www.cnblogs.com/Khada-Jhin/p/9302981.html
今日推荐