Description
有一个长度为n的数组{a1,a2,…,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
Solution
一眼看过去,很明显就是数据结构。
不过,用什么?怎么维护?
是个问题?
用什么
哎呀,发现很难找到一个可以在线的数据结构去维护。那么离线呢,好像离线可以。以下标线段树为节点的线段树,无法判重值,好像不能维护权值,想到不能维护权值,那么就有很多数据结构不能用了,维护权值的数据结构。
有什么数据结构是维护下标的吗,最显然的,函数式线段树。
怎么维护
函数式线段树在这题,很明显是维护下标的了。离线打法好!既然我们要用离线,如果是从左到右的,输入l[i],r[i](询问l[i]到r[i]的区间)那么就是以r[i]为关键字从小到大排序。那么我们在函数式线段树的加入中就锁定了r[i]了。
现在就很容易想了,比如说我现在要询问l到r中的答案,因为我们线段树的下标是权值,那么如果1到l的线段树中有一个下标是小于l的,那么答案就存在于这个区间(维护的是权值,越往左越小。下标小与l,那么肯定是l到r中没有的下标,而且肯定也没有这个值)。
那么很显然,我们维护的是在权值是l到r中出现过得最小下标。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=200007;
using namespace std;
int i,j,k,l,n,m,ber;
int a[maxn],ans[maxn];
struct nod{
int a,b,c;
}b[maxn];
int t[maxn*3];
bool cmp(nod x,nod y){
return x.b<y.b;
}
void insert(int x,int l,int r,int y,int z){
if(l==r){
t[x]=z;
}
else{
int mid=(l+r)/2;
if(y<=mid)insert(x*2,l,mid,y,z);
else insert(x*2+1,mid+1,r,y,z);
t[x]=min(t[x*2],t[x*2+1]);
}
}
int get(int x,int l,int r,int z){
if(l==r) return l;
int mid=(l+r)/2;
if(t[x*2]<z) return get(x*2,l,mid,z);
else return get(x*2+1,mid+1,r,z);
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,n){
scanf("%d",&a[i]);
if(a[i]>n+1)a[i]=n+1;
}
fo(i,1,m){
scanf("%d%d",&b[i].a,&b[i].b);
b[i].c=i;
}
sort(b+1,b+1+m,cmp);
ber=1;
fo(i,1,n){
insert(1,0,n+1,a[i],i);
fo(j,ber,m){
if(b[j].b>i){
ber=j;
break;
}
ans[b[j].c]=get(1,0,n+1,b[j].a);
}
}
fo(i,1,m) printf("%d\n",ans[i]);
}