多次询问一个区间内最小没有出现过的自然数
回滚莫队:发现添加一个数难以维护,故只用删除操作来维护答案。用cnt[]来计数,一开始把[1,n]全部塞进去,记录全局答案ANS,只有新的询问所属的块与上次询问不同时更新ANS(因为该询问前面的块已经没有用了);
什么时候更新答案ans呢?一开始ans = ANS的,每一次删除的时候,判断cnt[x]是否等于0,因为一开始cnt[]数组是记录了 [ 询问左区间的块的左区间,n] 之间的所有数字的,所以删着删着如果cnt[x] == 0了,那么ans = min(ans,x);
详细看代码;
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ll long long
#define ULL unsigned long long
#define Pair pair<LL,LL>
#define f1 first
#define f2 second
#define ls rt<<1
#define rs rt<<1|1
#define Pi acos(-1.0)
#define eps 1e-6
#define DBINF 1e100
#define mod 998244353
#define MAXN 1e18
#define MS 1000009
LL n,m;
LL a[MS]; // 原数组
struct node{
int l,r,id;
}ask[MS];
LL size,bknum; // 每一块的位置,总块数
LL bkl[MS],bkr[MS]; // 分别记录第i块的左右区间
LL belong[MS]; // 表示下标pos位置所属块号
LL ANS;
LL cnp[MS]; // 临时计数数组 用于暴力处理 cnp[i]表示i出现次数
LL cnt[MS]; // 用于计数数组 cnt[i]表示i出现次数
LL ac[MS]; // 记录答案
void init_ANS(){
for(int i=1;i<=n;i++){
if(a[i] > n+1) continue;
cnt[a[i]]++;
}
while(cnt[ANS]) ANS++;
}
void init_bk(){
size = sqrt(n);
bknum = n/size;
for(int i=1;i<=bknum;i++){
bkl[i] = (i-1)*size+1;
bkr[i] = i*size;
}
if(bkr[bknum] < n){
bknum++;
bkl[bknum] = bkr[bknum-1]+1;
bkr[bknum] = n;
}
for(int i=1;i<=bknum;i++){
for(int j=bkl[i];j<=bkr[i];j++){
belong[j] = i;
}
}
}
bool cmp(node t1,node t2){ // 按块排序,左区间从小到大,右区间从大到小
if(belong[t1.l] != belong[t2.l]) return t1.l < t2.l;
return t1.r > t2.r;
}
void add(int pos){
if(a[pos] > n+1) return;
cnt[a[pos]]++;
}
void remove(int pos,LL &ans){ // 删除时更新答案
if(a[pos] > n+1) return;
cnt[a[pos]]--;
if(cnt[a[pos]] == 0) ans = min(ans,a[pos]);
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
}
init_ANS(); // 初始化全局答案
for(int i=1;i<=m;i++){
int l,r;
cin >> l >> r;
ask[i] = {l,r,i};
}
init_bk(); // 初始化 块
sort(ask+1,ask+m+1,cmp); // 对询问按 块 排序
int L = 1 ,R = n;
int lastbk = 0;
LL ans = ANS;
for(int i=1;i<=m;i++){
if(belong[ask[i].l] == belong[ask[i].r]){ // 属于同一块 暴力处理
for(int j=ask[i].l;j<=ask[i].r;j++){
if(a[j] > n+1) continue;
cnp[a[j]]++;
}
LL tmp = 0;
while(cnp[tmp]) tmp++;
ac[ask[i].id] = tmp;
for(int j=ask[i].l;j<=ask[i].r;j++){
if(a[j] > n+1) continue;
cnp[a[j]]--;
}
continue;
}
if(belong[ask[i].l] != lastbk){ // 新的左区间与上次的左区间块不同
while(R < n) add(++R);
while(L < bkl[belong[ask[i].l]]) remove(L++,ANS); // ***更新全局ANS
lastbk = belong[ask[i].l];
ans = ANS;
}
while(R > ask[i].r){
remove(R--,ans);
}
LL tmp = ans; // 左区间可能是乱序的,tmp作为临时记录
while(L < ask[i].l){
remove(L++,tmp);
}
ac[ask[i].id] = tmp;
// 回滚
while(L > bkl[belong[ask[i].l]]) add(--L);
}
for(int i=1;i<=m;i++){
cout << ac[i] << "\n";
}
return 0;
}
原文:https://www.cnblogs.com/Tecode/p/14753060.html