BZOJ 3585:mex

Portal:http://www.lydsy.com/JudgeOnline/problem.php?id=3585

解析:一开始我想的是用莫队+Set做,复杂度是\(O(nlogn\sqrt n)\),结果居然跑过了(当然跑了倒数第一名),后来参考别人的做法发现可以用按权值分块,因为最终的答案一定不会大于区间长度,所以对于每个权值按\(\sqrt n\)分块,分别维护块内得到数字个数和每个权值的出现次数,对于每个询问在\(O(\sqrt n)\)的额外时间内求得答案。


代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm> 
#include <vector> 
 
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())
 
using namespace std;
 
typedef long long LL;
 
const int maxn=210000+23;
 
inline int read() {
 int x=0; char c=getchar();
 while ((c<'0')||(c>'9')) c=getchar();
 while (('0'<=c)&&(c<='9')) x=x*10+(int)(c-'0'),c=getchar();
 return x;
}
 
int n,m,B,size,k,L,R;
int a[maxn],ql[maxn],qr[maxn],head[maxn],next[maxn],answer[maxn],vis[maxn];
int num[maxn],block[maxn];
 
int main()
{
 n=read(),m=read();
 
 for (B=1;B*B<=n;B++);
 B--,size=(n-1)/B+1;
 
 rep(i,1,n)
 {
  a[i]=read();
  if (a[i]>n) a[i]=n+B+1;
  num[i]=(a[i])/B+1;
 }
 
 for (B=1;B*B<=n;B++);
 B--,size=(n-1)/B+1;
  
 rep(i,1,m)
 {
  ql[i]=read(),qr[i]=read();
   
  k=(ql[i]-1)/B*size+(qr[i]-1)/B+1;
  next[i]=head[k];
  head[k]=i;  
 }
 
 L=1,R=n;
 rep(i,1,n)
 {
  vis[a[i]]++;
  if (vis[a[i]]==1) block[num[i]]++;
 }
 
 rep(T,1,size*size)
  for (int i=head[T];i;i=next[i])
  {
   while (ql[i]<L)
   {
    L--,vis[a[L]]++;
    if (vis[a[L]]==1) block[num[L]]++;
   }
 
   while (R<qr[i])
   {
    R++,vis[a[R]]++;
    if (vis[a[R]]==1) block[num[R]]++;
   }
 
   while (L<ql[i])
   {
    if (vis[a[L]]==1) block[num[L]]--;
    vis[a[L]]--,L++;
   }
 
   while (qr[i]<R)
   {
    if (vis[a[R]]==1) block[num[R]]--;
    vis[a[R]]--,R--;
   }
    
   for (k=1;(block[k]==B);k++);
   rep(j,(k-1)*B,k*B-1) if (vis[j]==0) {answer[i]=j;break;}
  }
  
 rep(i,1,m) printf("%d\n",answer[i]);
  
 return 0;
}

这也给了我一个启示:莫队并不一定要在\(O(1)\)时间内得到当前答案,可以通过维护其他数据结构来计算答案。

posted @ 2017-01-31 17:10  Krew  阅读(119)  评论(0)    收藏  举报