分析
我们建立主席树,对于第\(r\)棵线段树每一个位置\(i\)存储的是数字\(i\)出现的最右端的位置。
那么对于区间询问\([l,r]\),就是找到第\(r\)棵线段树(注意不是差分)的第一个值小于\(l\)的位置(不可能大于\(r\))。
这个很容易,我们只需要维护区间最小值,询问时如果左区间的区间最小值小于\(l\)说明\(mex\)在左区间否则\(mex\)在右区间。
时间复杂度\(O(N \log V+M \log V)\),空间复杂度\(O(N \log V)\)。
代码
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x){
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-'0',ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;
const int MAXN=2e5+1,MAXM=6000000,MAXD=1e8;//5979470.5707972522261665749730809
int root[MAXN];
struct PreSegTree
{
int cnt;
int minv[MAXM];
int L[MAXM],R[MAXM];
void pushup(int o)
{
minv[o]=min(minv[L[o]],minv[R[o]]);
}
void insert(int&now,int l,int r,int p,int v)
{
minv[++cnt]=minv[now];
L[cnt]=L[now],R[cnt]=R[now];
now=cnt;
if(l==r)
{
minv[now]=v;
return;
}
int mid=(l+r)>>1;
if(p<=mid)
insert(L[now],l,mid,p,v);
else
insert(R[now],mid+1,r,p,v);
pushup(now);
}
int query(int now,int l,int r,int x)
{
if(l==r)
return l;
int mid=(l+r)>>1;
if(minv[L[now]]>=x) // 第一个小于x的位置
return query(R[now],mid+1,r,x);
else
return query(L[now],l,mid,x);
}
}T;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,m;
read(n);read(m);
for(int i=1;i<=n;++i)
{
int a;
read(a);
root[i]=root[i-1];
T.insert(root[i],0,MAXD,a,i);
}
while(m--)
{
int l,r;
read(l);read(r);
printf("%d\n",T.query(root[r],0,MAXD,l));
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
Hint
尽管题目\(a\)范围为\(10^9\),但我MAXD
开1e9
过不了开1e8
却能过,不知道为什么。
或许题目有变动?反正我看不见这道权限题。