莫队 学习笔记

阅前声明

题单
为方便,题目链接均在洛谷。

要用桶的时候请尽量不要使用 map 或者 un_map,会造成不必要的 TLE。

普通莫队

当一个区间问题,可以由 [l,r] 转移到 [l±1,r][l,r±1],且添加、删除都可以已很快的时间完成时,我们可以使用莫队算法。
我们先将询问离线下来,并将他们排序。我们先把数列分为若干个块,块长为 S,则先按 l 所在的区间排序,再按 r 的大小排序。
对于每一个询问,我们对于上个区间暴力转移到下个区间。可行转移方式之一是先扩大区间再缩小区间。
S=n 时,时间复杂度为 nn

莫队的主体,按上面实现的话,应该是长这样:

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 前,会对答案产生 cntx 的贡献。
  • 删除 x 后,会对答案造成 cntx 的贡献。
点击查看代码
#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

首先我们知道 是可以前缀和的。
然后求一段区间 [l,r] 的异或和可以转换为前缀异或数组 ssrsl1
所以答案变为求 [l,r]sxsy=k(xy){x,y} 对数。
我们用一个桶 cnt 来保存每个数的出现次数,那么:

  • 加入 x 前,会对答案产生 cntxk 的贡献。
  • 删除 x 后,会对答案产生 cntxk 的贡献。
点击查看代码
#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] 众数的出现次数。

我们用一个 cntx 记录 x 的出现次数,用一个 ty 来表示出现次数 y 的个数。
那么我们有:

  • 每一次插入后,答案 ans 可以更新为 max(ans,tcntx)
  • 每一次删除前,如果答案为 cntx=anstcntx=1,那么 ansans1。(因为区间众数次数减一一定会有数,比如说这个 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±1,r,t],[l,r±1,t],[l,r,t±1]
转移顺序 lr 其实不变,时间放在最后即可,因为时间不是一个区间,没有什么要考虑的。
块长取 n23 时较优,时间复杂度 O(n53)
那么他应该长这样:

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] 中每个数字出现的次数的 mex

正常带修莫队。维护每个数出现的次数即可。至于查询,因为出现次数为 i 次的数会占掉 i 个位置,所以询问的时候答案最多不会超过 n 个。所以直接暴力做即可,时间复杂度 O(n53)

点击查看代码
#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

莫队好题。
首先我们依旧是记录一个 cnti 表示元素 i 出现的次数,cntti 表示出现次数为 i 次的数的个数。
每一次查询就相当于在 cntt 中找一段最短的且区间加和 k 的区间的长度,可以用双指针来写。
但是因为 cntt 中有数的不会太多,我们可以对他值域分块,对于每个块,如果有数,那么遍历,否则跳过。块长取 n53 即可。
时间复杂度 O(n53)

点击查看代码
#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;
}
posted @   coding_goat_qwq  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示