题目链接:http://codeforces.com/contest/940/problem/F
CF Round #466的最后一题,颇有难度,正解是带修改莫队算法。
【题意】
给定一个长度为nn的数组aa,并且要求执行qq个操作,有两种不同的操作:
①询问一个区间[l,r][l,r]中集合{c0,c1,c2,⋯,c109}{c0,c1,c2,⋯,c109}的Mex,而cici表示数值ii在[l,r][l,r]中的出现次数。
②把apap修改成xx。
对每一个询问输出答案。
【题解】
典型的区间问题,不要求在线,可以考虑莫队。
有时间轴影响,故使用带修改莫队,时间复杂度应为O(n53)O(n53)。
先对区间的移动进行分析:
使用离散化技巧,把输入数据压缩至n+qn+q的范围内。
维护两个数组count1,count2count1,count2,count1count1记录(离散后的)每个数的出现次数,count2count2记录count1count1中的数的出现次数。
那么所求为count2count2中第一个为0的下标位置。
对于count1,count2count1,count2,我们都可以O(1)O(1)维护每个操作对数组的影响,接下来考虑如何计算答案。
count2count2数组的变动,让第一个为0的下标位置可能会有很大的跳跃,不好维护,那么我们注意到一个性质:
答案不会超过O(n−−√)O(n),为什么呢?
假如要将count21,count22,⋯,count2kcount21,count22,⋯,count2k填满的话,至少需要k(k+1)2k(k+1)2个元素,可是数组的总长只有nn,所以答案必然不能太大。
那么有了这个性质,可以暴力维护答案,维护答案的总的复杂度不会超过O(qn−−√)O(qn)。
关于莫队,还有几个需要注意的地方:
第一个是当维护区间变化时,先考虑"伸展",再考虑"压缩",要不然会出现区间r<lr<l的情况。
一般的莫队不会太在意这个,因为后面会再加回来,但是这题中可能会导致中间结果多减了,导致count2count2数组越界。
第二个是在带修改莫队时间轴移动上,千万不要颠倒了时间顺序,这其实也是常识了,不过我被这个卡了一会儿。
#include <bits/stdc++.h>
using namespace std;
const int N = 2E5 + 7;
map<int,int>mp;
int cnt[N], vis[N], a[N];
int BLOCK;
int ans[N];
int num[N], tot;
int n, q;
int cntq ;
int cntc ;
struct QQ
{
int l, r, z, id;
}Q[N];
struct CC
{
int x, y;
}C[N];
bool cmp(QQ a, QQ b)
{
if(a.l / BLOCK == b.l / BLOCK) {
if(a.r / BLOCK == b.r / BLOCK) return a.z < b.z;
return a.r < b.r;
} else {
return a.l < b.l;
}
}
void add(int x)
{
--vis[cnt[num[x]]];
++cnt[num[x]];
++vis[cnt[num[x]]];
}
void dec(int x)
{
--vis[cnt[num[x]]];
--cnt[num[x]];
++vis[cnt[num[x]]];
}
void solve()
{
int l = 1, r = 1, c = 0, res;
add(1);
for(int i = 1;i <= cntq; ++ i) {
while(r < Q[i].r) add(++r);
while(r > Q[i].r) dec(r--);
while(l < Q[i].l) dec(l++);
while(l > Q[i].l) add(--l);
while(c > Q[i].z){
if(C[c].x >= l && C[c].x <= r) dec(C[c].x);
swap(C[c].y, num[C[c].x]); //重点
if(C[c].x >= l && C[c].x <= r) add(C[c].x);
c--;
}
while(c < Q[i].z){
c++;
if(C[c].x >= l && C[c].x <= r) dec(C[c].x);
swap(C[c].y, num[C[c].x]);
if(C[c].x >= l && C[c].x <= r) add(C[c].x);
}
res = 1;
while(vis[res]) ++res;
ans[Q[i].id] = res;
}
for(int i = 1;i <= cntq;i ++) printf("%d\n",ans[i]);
}
int main()
{
scanf("%d %d", &n, &q);
BLOCK = (int)pow(n, 2.0/3.0);
for(int i = 1;i <= n; ++ i) {
int x;
scanf("%d", &x);
if(mp[x]) num[i] = mp[x];
else num[i] = mp[x] = ++tot;
}
int opt, l, r;
for(int i = 1;i <= q; ++ i) {
scanf("%d %d %d", &opt, &l, &r);
if(opt == 1) {
++ cntq;
Q[cntq] = (QQ){l,r,cntc,cntq};
} else {
++ cntc;
if(mp[r]) r = mp[r];
else r = mp[r] = ++tot;
C[cntc] = (CC){l, r};
}
}
sort(Q+1,Q+1+cntq,cmp);
solve();
return 0;
}