题意:
给出一个长度为n的自然数序列,m次查询某一区间[l,r]中数集的mex函数值;
n,m<=200000,0<=a[i]<=10^9;
题解:
mex这个东西似乎并不能直接用某些数据结构维护;
首先实际上a[i]太大是没有用的,因为如果在首页数字中隔开了一段,那么比那个数大的数不可能对答案有影响;
这样我们就相当于将所有数离散到了200000的级别;
然后利用莫队算法维护当前区间数的集合, 那之后就是查询第一个未覆盖的点了;
一开始脑补的是用一个堆来维护这东西,修改O(logn)查询O(1);
总复杂度是O(m+m√n*logn),用线段树实现了一发TLE了;
我们需要一种能O(1)完成修改的数据结构——分块!
记录一个块内覆盖了多少元素,查询时先找到第一个未完全覆盖的块,再找到块内第一个未覆盖的点;
这样时间复杂度就是O(m√n+m√n)=O(m√n)了;
BZOJ3339双倍经验;
代码:
#include<cctype> #include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 210000 #define LEN 1<<16 #define lson l,mid,no<<1 #define rson mid+1,r,no<<1|1 using namespace std; char getc() { static char *S,*T,buf[LEN]; if(S==T) { T=(S=buf)+fread(buf,1,LEN,stdin); } return *S++; } int read() { static char ch; static int D; while(!isdigit(ch=getc())); for(D=ch-'0';isdigit(ch=getc());) D=D*10+ch-'0'; return D; } struct Query { int l,r,pos,no; friend bool operator <(Query a,Query b) { if(a.pos==b.pos) return a.r<b.r; return a.pos<b.pos; } }Q[N]; int a[N],dis[N]; int ma,bk,l,r; int c[N],ans[N],cnt[N]; bool cov[N]; void Change(int x,int f) { if(a[x]>ma) return ; if(!c[a[x]]) cov[a[x]]=1,cnt[a[x]/bk]++; c[a[x]]+=f; if(!c[a[x]]) cov[a[x]]=0,cnt[a[x]/bk]--; } int query() { int i; for(i=0;cnt[i]==bk;i++); for(i=i*bk;;i++) if(!cov[i]) return i; } int main() { int n,m,i,j,k; n=read(),m=read(); bk=sqrt(n); for(i=1;i<=n;i++) dis[i]=a[i]=read(); sort(dis+1,dis+n+1); for(i=1,ma=-1;i<=n;i++) { if(dis[i]==ma+1) ma++; else if(dis[i]>ma+1) break; } ma++; for(i=1;i<=m;i++) { Q[i].l=read(),Q[i].r=read(); Q[i].pos=Q[i].l/bk; Q[i].no=i; } sort(Q+1,Q+m+1); for(i=1,l=1,r=0;i<=m;i++) { while(l>Q[i].l) Change(--l,1); while(r<Q[i].r) Change(++r,1); while(l<Q[i].l) Change(l++,-1); while(r>Q[i].r) Change(r--,-1); ans[Q[i].no]=query(); } for(i=1;i<=m;i++) { printf("%d\n",ans[i]); } return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/49357345