链接
通过可持久化建n棵权值线段树,因为最小未出现自然数一定小于数列长度所以权值线段树长度为2e5+1即可,储存每个数最后出现的位置,每次查询l,r,即查询第r棵线段树第一个出现位置小于l的数即可,为了查询还需要保存区间最小值,查询时最小值小于l就往哪边继续查。
总结一下:1可持久化。2利用线段树可查询第一个权值小于k的数。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
struct node
{
int l,r,mi;
}tree[30*maxn];
int cnt=0;
int cnode(int s)
{
tree[++cnt]=tree[s];
return cnt;
}
int build(int l,int r)
{
int p=++cnt;
if(l==r)
{
tree[p].mi=0;
}
else
{
int mid=l+(r-l)/2;
tree[p].l=build(l,mid);
tree[p].r=build(mid+1,r);
tree[p].mi=min(tree[tree[p].l].mi,tree[tree[p].r].mi);
}
return p;
}
int update(int s,int l,int r,int w,int v)
{
int p=cnode(s);
if(l==r)
{
tree[p].mi=w;
return p;
}
int mid=l+(r-l)/2;
if(mid>=v)
tree[p].l=update(tree[p].l,l,mid,w,v);
else
tree[p].r=update(tree[p].r,mid+1,r,w,v);
tree[p].mi=min(tree[tree[p].l].mi,tree[tree[p].r].mi);
return p;
}
int query(int p,int l,int r,int v)
{
if(l==r)
{
return l;
}
else
{
int mid=l+(r-l)/2;
if(tree[tree[p].l].mi<v)
return query(tree[p].l,l,mid,v);
else
{
return query(tree[p].r,mid+1,r,v);
}
}
}
int a[maxn],rt[maxn];
int main()
{
// freopen("tte.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int ro=build(0,200000);
rt[0]=ro;
for(int i=1;i<=n;i++)
{
if(a[i]<=n)
rt[i]=update(rt[i-1],0,200000,i,a[i]);
else rt[i]=rt[i-1];
}
for(int i=1;i<=m;i++)
{
int ql,qr;
scanf("%d%d",&ql,&qr);
int ans=query(rt[qr],0,200000,ql);
printf("%d\n",ans);
}
return 0;
}