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)\)时间内得到当前答案,可以通过维护其他数据结构来计算答案。

浙公网安备 33010602011771号