莫队 学习笔记
阅前声明
题单
为方便,题目链接均在洛谷。
要用桶的时候请尽量不要使用 map 或者 un_map,会造成不必要的 TLE。
普通莫队
当一个区间问题,可以由 \([l,r]\) 转移到 \([l\pm 1,r]\) 和 \([l,r\pm 1]\),且添加、删除都可以已很快的时间完成时,我们可以使用莫队算法。
我们先将询问离线下来,并将他们排序。我们先把数列分为若干个块,块长为 \(S\),则先按 \(l\) 所在的区间排序,再按 \(r\) 的大小排序。
对于每一个询问,我们对于上个区间暴力转移到下个区间。可行转移方式之一是先扩大区间再缩小区间。
当 \(S=\sqrt{n}\) 时,时间复杂度为 \(n\sqrt{n}\)。
莫队的主体,按上面实现的话,应该是长这样:
int l=1,r=0;
For(i,1,m) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].idx]=sum;
}
DQUERY - D-query
题意:区间不同数
莫队主体是简单的,于是我们考虑加入和删除。
我们可以开一个桶保存每个数出现的次数:
- 加入时如果保存该数的桶为空,那么答案加 \(1\)。
- 删除后如果保存该数的桶为空,那么答案减 \(1\)。
- 然后对桶进行操作即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 200050
int n,m,siz;
map<int,int> cnt;
int a[maxn];
struct node{
int l,r,idx;
bool operator<(const node &x) {
return l/siz==x.l/siz?r<x.r:l<x.l;
}
}q[maxn];
int sum=0;
void upd(int x) {
if(cnt[x]==0) sum++;
cnt[x]++;
}
void del(int x) {
if(cnt[x]==1) sum--;
cnt[x]--;
}
int ans[maxn];
void work() {
in1(n);
siz=sqrt(n);
For(i,1,n) in1(a[i]);
in1(m);
For(i,1,m) in2(q[i].l,q[i].r),q[i].idx=i;
sort(q+1,q+m+1);
int l=q[1].l,r=q[1].l-1;
For(i,1,m) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].idx]=sum;
}
For(i,1,m) cout<<ans[i]<<'\n';
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
P1494 [国家集训队] 小 Z 的袜子
求区间 \([l,r]\) 中随机选出两个数相等的概率。
简单计数题。
用一个桶 \(cnt\) 保存每一个数出现的次数,考虑一个数 \(x\) 的贡献。
- 加入 \(x\) 前,会对答案产生 \(cnt_x\) 的贡献。
- 删除 \(x\) 后,会对答案造成 \(-cnt_x\) 的贡献。
点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 50040
int n,m,siz;
int a[maxn],cnt[maxn];
struct quest{
int l,r,idx;
}q[maxn];
bool cmp(quest a,quest b) {
return a.l/siz==b.l/siz?a.r<b.r:a.l<b.l;
}
pair<int,int> ans[maxn];
int sum=0;
void upd(int x) {sum+=cnt[x]; cnt[x]++; }
void del(int x) {cnt[x]--; sum-=cnt[x]; }
void work() {
in2(n,m);
For(i,1,n) in1(a[i]);
For(i,1,m)
in2(q[i].l,q[i].r),q[i].idx=i;
siz=sqrt(n);
sort(q+1,q+m+1,cmp);
int l=q[1].l,r=q[1].l-1;
For(i,1,m) {
if(q[i].l==q[i].r) {
ans[q[i].idx].first=0;
ans[q[i].idx].second=1;
continue ;
}
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].idx].first=sum;
ans[q[i].idx].second=(r-l+1)*(r-l)/2;
if(!ans[q[i].idx].first) ans[q[i].idx].second=1;
}
For(i,1,m) {
int G=__gcd(ans[i].first,ans[i].second);
cout<<ans[i].first/G<<'/'<<ans[i].second/G<<'\n';
}
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
XOR and Favorite Number
询问 \([l,r]\) 中有多少个区间满足区间的异或和为 \(k\)。
首先我们知道 \(\oplus\) 是可以前缀和的。
然后求一段区间 \([l,r]\) 的异或和可以转换为前缀异或数组 \(s\) 的 \(s_{r}\oplus s_{l-1}\)。
所以答案变为求 \([l,r]\) 中 \(s_x \oplus s_y = k(x\le y)\) 的 \(\{x,y\}\) 对数。
我们用一个桶 \(cnt\) 来保存每个数的出现次数,那么:
- 加入 \(x\) 前,会对答案产生 \(cnt_{x\oplus k}\) 的贡献。
- 删除 \(x\) 后,会对答案产生 \(-cnt_{x\oplus k}\) 的贡献。
点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 100050
int n,m,k,siz;
int a[maxn];
int cnt[maxn*20];
ll sum=0;
struct quest{
int l,r,idx;
bool operator<(const quest &x) const{
return l/siz==(x.l/siz)?r<x.r:l<x.l;
}
}q[maxn];
void upd(int x) {
sum+=cnt[x^k];
cnt[x]++;
}
void del(int x) {
cnt[x]--;
sum-=cnt[x^k];
}
int ans[maxn];
void work() {
in3(n,m,k);
siz=sqrt(n);
For(i,1,n) in1(a[i]);
For(i,1,n) a[i]^=a[i-1];
For(i,1,m) {
in2(q[i].l,q[i].r);
q[i].l--;//前缀和导致的
q[i].idx=i;
}
sort(q+1,q+m+1);
int l=1,r=0;
For(i,1,m) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].idx]=sum;
}
For(i,1,m) cout<<ans[i]<<'\n';
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
P3709 大爷的字符串题
询问区间 \([l,r]\) 众数的出现次数。
我们用一个 \(cnt_x\) 记录 \(x\) 的出现次数,用一个 \(t_y\) 来表示出现次数 \(y\) 的个数。
那么我们有:
- 每一次插入后,答案 \(ans\) 可以更新为 \(\max(ans,t_{cnt_x})\);
- 每一次删除前,如果答案为 \(cnt_x = ans\) 且 \(t_{cnt_x} = 1\),那么 \(ans \to ans-1\)。(因为区间众数次数减一一定会有数,比如说这个 \(x\))。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 200050
int n,m,siz;
int a[maxn],b[maxn];
struct node {
int l,r,idx;
bool operator<(const node &x) const {
if(l/siz!=x.l/siz) return l<x.l;
if((l/siz)&1) return r<x.r;
return r>x.r;
}
}q[maxn];
int cnt[maxn];
int t[maxn];
int mx;
int ans[maxn];
void upd(int x) {
t[cnt[x]]--;
t[++cnt[x]]++;
mx=max(mx,cnt[x]);
}
void del(int x) {
t[cnt[x]]--;
if(cnt[x]==mx&&t[cnt[x]]==0) mx--;
t[--cnt[x]]++;
}
void work() {
in2(n,m);
siz=sqrt(n);
For(i,1,n) in1(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int len=unique(b+1,b+n+1)-b;
For(i,1,n) a[i]=lower_bound(b+1,b+len+1,a[i])-b;
For(i,1,m) {
in2(q[i].l,q[i].r);
q[i].idx=i;
}
sort(q+1,q+m+1);
int l=1,r=0;
For(i,1,m) {
// cerr<<i<<" qwq\n";
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
ans[q[i].idx]=mx;
}
For(i,1,m) cout<<-ans[i]<<'\n';
}
signed main() {
// freopen("P3709_2.in","r",stdin);
// freopen("qwq.out","w",stdout);
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
P3245 [HNOI2016] 大数 【未完工】
未完工 qwq。
带修莫队
对于单点修改的题,我们给莫队强行安一个时间的维度,那么一个询问 \([l,r,t]\) 可以转到 \([l\pm 1,r,t],[l,r\pm 1,t],[l,r,t\pm 1]\)。
转移顺序 \(l\) 和 \(r\) 其实不变,时间放在最后即可,因为时间不是一个区间,没有什么要考虑的。
块长取 \(n^{\frac23}\) 时较优,时间复杂度 \(O(n^{\frac53})\)。
那么他应该长这样:
int l=1,r=0,t=0;
For(i,1,tot) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
while(t>q[i].t) {
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second); t--;
}
while(t<q[i].t) { t++;
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second);
}
ans[q[i].idx]=sum;
}
P1903 [国家集训队] 数颜色 / 维护队列
单点修改区间不同数
和区间不同数大抵相同,略过。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 200050
int n,m,siz;
int a[maxn];
pair<int,int> ti[maxn]; int top=1,tot;
struct node {
int l,r,t,idx;
bool operator<(const node &x) const {
if(l/siz!=x.l/siz) return l<x.l;
if(r/siz!=x.r/siz) return r<x.r;
return ((r/siz)&1)?t<x.t:t>x.t;
}
}q[maxn];
int cnt[maxn*5],sum;
int ans[maxn];
void upd(int x) {cnt[x]++; if(cnt[x]==1) sum++; }
void del(int x) {if(cnt[x]==1) sum--; cnt[x]--; }
void work() {
cin>>n>>m;
For(i,1,n) cin>>a[i];
For(i,1,m) {
char ch;
cin>>ch;
if(ch=='Q') {
tot++;
cin>>q[tot].l>>q[tot].r;
q[tot].t=top;
q[tot].idx=tot;
} else {
top++;
cin>>ti[top].first>>ti[top].second;
}
}
siz=pow(n,2.0/3)*pow(top,1.0/3)/pow(m,1.0/3);
sort(q+1,q+tot+1);
int l=1,r=0,t=0;
For(i,1,tot) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
while(t>q[i].t) {
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second); t--;
}
while(t<q[i].t) { t++;
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second);
}
ans[q[i].idx]=sum;
}
For(i,1,tot) cout<<ans[i]<<'\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
CF940F Machine Learning
单点修改,求 \([l,r]\) 中每个数字出现的次数的 \(\operatorname{mex}\)。
正常带修莫队。维护每个数出现的次数即可。至于查询,因为出现次数为 \(i\) 次的数会占掉 \(i\) 个位置,所以询问的时候答案最多不会超过 \(\sqrt{n}\) 个。所以直接暴力做即可,时间复杂度 \(O(n^{\frac{5}{3}})\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 200050
int n,m,siz;
int a[maxn],b[maxn];
pair<int,int> ti[maxn]; int top=1,tot;
struct node {
int l,r,t,idx;
bool operator<(const node &x) const {
if(l/siz!=x.l/siz) return l<x.l;
if(r/siz!=x.r/siz) return r<x.r;
return ((r/siz)&1)?t<x.t:t>x.t;
}
}q[maxn];
int cnt[maxn*5],sum,T[maxn];
int ans[maxn];
void upd(int x) {T[cnt[x]]--; ++cnt[x]; T[cnt[x]]++; }
void del(int x) {T[cnt[x]]--; --cnt[x]; T[cnt[x]]++; }
void work() {
cin>>n>>m;
For(i,1,n) cin>>a[i],b[i]=a[i];
int ttt=n;
siz=pow(n,2.0/3);
For(i,1,m) {
int opt;
cin>>opt;
if(opt==1) {
tot++;
cin>>q[tot].l>>q[tot].r;
q[tot].t=top;
q[tot].idx=tot;
} else {
top++;
cin>>ti[top].first>>ti[top].second;
b[++ttt]=ti[top].second;
}
}
sort(b+1,b+ttt+1);
int len=unique(b+1,b+ttt+1)-b-1;
For(i,1,n) a[i]=lower_bound(b+1,b+len+1,a[i])-b;
For(i,1,top) ti[i].second=lower_bound(b+1,b+len+1,ti[i].second)-b;
sort(q+1,q+tot+1);
int l=1,r=0,t=0;
For(i,1,tot) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
while(t>q[i].t) {
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second); t--;
}
while(t<q[i].t) { t++;
if(ti[t].first>=q[i].l&&ti[t].first<=q[i].r)
del(a[ti[t].first]),upd(ti[t].second);
swap(a[ti[t].first],ti[t].second);
}
sum=1;
For(i,1,n) if(!T[i]) break; else sum=i+1;
ans[q[i].idx]=sum;
}
For(i,1,tot) cout<<ans[i]<<'\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
CF1476G Minimum Difference
莫队好题。
首先我们依旧是记录一个 \(cnt_i\) 表示元素 \(i\) 出现的次数,\(cntt_i\) 表示出现次数为 \(i\) 次的数的个数。
每一次查询就相当于在 \(cntt\) 中找一段最短的且区间加和 \(\le k\) 的区间的长度,可以用双指针来写。
但是因为 \(cntt\) 中有数的不会太多,我们可以对他值域分块,对于每个块,如果有数,那么遍历,否则跳过。块长取 \(n^{\frac53}\) 即可。
时间复杂度 \(O(n^{\frac{5}{3}})\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first
#define scd second
#define dbg puts("IAKIOI")
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 200050
int n,m,siz;
int a[maxn];
// --- Moque ---
struct node {
int l,r,t,k,idx;
bool operator<(const node & x ) const {
if(l/siz!=x.l/siz) return l<x.l;
if(r/siz!=x.r/siz) return r<x.r;
if(t/siz==x.t/siz) {
if((t/siz)&1) return t<x.t;
return t>x.t;
} else return t<x.t;
}
}q[maxn];
int ans[maxn];
pair<int,int> ti[maxn];
int top1,top2;
int cnt[maxn],cntt[maxn];// cnt 记录 i 的出现次数,cntt 记录出现次数为 i 的个数
// --- Moque ---
// --- Block ---
#define maxb 350
int bel[maxn],bsiz;// 每个点属于哪个块,有多少个块
int bl[maxb],br[maxb];// 每个块的最左/右
int bcnt[maxb];// 每个块大小
int c[maxn],topc,reidx[maxn];
// --- Block ---
// --- Moque ---
void upd(int x) {
cntt[cnt[x]]--; bcnt[bel[cnt[x]]]--;
cnt[x]++;
cntt[cnt[x]]++; bcnt[bel[cnt[x]]]++;
}
void del(int x) {
cntt[cnt[x]]--; bcnt[bel[cnt[x]]]--;
cnt[x]--;
cntt[cnt[x]]++; bcnt[bel[cnt[x]]]++;
}
// --- Moque ---
int query(int k) {
topc=0;
For(i,1,bsiz) if(bcnt[i]) {
For(j,bl[i],br[i]) if(cntt[j]) {
c[++topc]=cntt[j];
reidx[topc]=j;
}
}
int ans=1e9;
int i=1,j=0,sum=0;
while(i<=topc) {
while(j<topc&&sum<k) sum+=c[++j];
if(sum>=k) ans=min(ans,reidx[j]-reidx[i]);
else break;
sum-=c[i++];
}
if(ans==1e9) ans=-1;
return ans;
}
void work() {
in2(n,m); siz=pow(n,2.0/3);
bsiz=n/siz;
For(i,1,bsiz) bl[i]=br[i-1]+1,br[i]=br[i-1]+siz;
br[bsiz]=n;
// For(i,1,bsiz) cout<<bl[i]<<' '<<br[i]<<'\n';
For(i,1,bsiz) For(j,bl[i],br[i]) bel[j]=i;
// For(i,1,n) cout<<bel[i]<<' ';
For(i,1,n) in1(a[i]);
For(i,1,m) {
int opt=read();
if(opt==1) {
in3(q[++top1].l,q[top1].r,q[top1].k);
q[top1].idx=top1;
q[top1].t=top2;
} else {
in2(ti[++top2].fst,ti[top2].scd);
}
}
sort(q+1,q+top1+1);
int l=1,r=0,t=0;
For(i,1,top1) {
while(l>q[i].l) upd(a[--l]);
while(r<q[i].r) upd(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
while(t>q[i].t) {
if(ti[t].fst>=q[i].l&&ti[t].fst<=q[i].r)
del(a[ti[t].fst]),upd(ti[t].scd);
swap(a[ti[t].fst],ti[t].scd); t--;
}
while(t<q[i].t) { t++;
if(ti[t].fst>=q[i].l&&ti[t].fst<=q[i].r)
del(a[ti[t].fst]),upd(ti[t].scd);
swap(a[ti[t].fst],ti[t].scd);
}
ans[q[i].idx]=query(q[i].k);
}
For(i,1,top1) cout<<ans[i]<<'\n';
}
signed main() {
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
int _=1;
// _=read();
For(i,1,_) {
work();
}
return 0;
}
本文来自博客园,作者:coding_goat_qwq,转载请注明原文链接:https://www.cnblogs.com/CodingGoat/p/18620909

浙公网安备 33010602011771号