人生有信仰 数据有梯度 暴力不爆零


bzoj 3585: mex && 3339: Rmq Problem -- 主席树

3585: mex

Time Limit: 20 Sec  Memory Limit: 128 MB

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

1
2
3
0
3

HINT

 

数据规模和约定

  对于100%的数据:

  1<=n,m<=200000

  0<=ai<=109

  1<=l<=r<=n


  对于30%的数据:


  1<=n,m<=1000

 

Source

By 佚名提供

 

我们考虑建权值线段树,每个数字 x 保存它最后出现的位置

这样查询[l,r],就是找第r棵主席树中第一个值< l 的

主席树上每个区间维护当前数中,权值从 l 到 r 中最后一次出现最靠左的位置

就是相当于维护区间最小值即可

 

#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010
#define M 6000100
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int rt[N],ls[M],rs[M],mn[M],cnt;
void add(int &p,int f,int l,int r,int x,int v)
{
    p=++cnt;
    if(l==r){mn[p]=v;return;}
    ls[p]=ls[f];rs[p]=rs[f];
    int mid=(l+r)>>1;
    if(x>mid) add(rs[p],rs[f],mid+1,r,x,v);
    else add(ls[p],ls[f],l,mid,x,v);
    mn[p]=min(mn[ls[p]],mn[rs[p]]);
}
int query(int p,int l,int r,int v)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(mn[ls[p]]<v) return query(ls[p],l,mid,v);
    else return query(rs[p],mid+1,r,v);
}
int n,m,x,mx=1e8+5;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++){x=read();add(rt[i],rt[i-1],0,mx,x,i);}
    int l,r;
    for(int i=1;i<=m;i++)
    {
        l=read();r=read();
        printf("%d\n",query(rt[r],0,mx,l));
    }
    return 0;
}

 

posted @ 2017-04-27 20:18  lkhll  阅读(223)  评论(0编辑  收藏  举报