带修莫队(Mo‘s algorithm with modifications)

前置知识:普通莫队

可以看我的博客莫队基础(Mo‘s algorithm)

带修莫队的核心思想

带修莫队(Mo’s Algorithm with Updates)是普通莫队算法的扩展,用于处理带修改的区间查询问题。其核心思想是在普通莫队二维(左端点 l l l、右端点 r r r)的基础上,引入第三维——时间 t t t,将每个查询与一个“时间戳”关联,从而在离线处理时能够正确地回退或应用修改操作。

带修莫队的基本实现步骤:

【模板】带修莫队 / [国家集训队] 数颜色 / 维护队列

1.定块长

本题将块长定为 n 2 ÷ 3 n^{2\div3} n2÷3 时间复杂度较 n \sqrt n n 优。
核心代码:

len=pow(n,2.0/3.0);

2.排序

就是如果 l l l 在的块是奇数块,那么 r r r 按照从小到大排序,否则按照大从小排序。

struct query{int l,r,t,id;}q[N];//询问
bool cmp(query x,query y)
{
	return pos[x.l]!=pos[y.l]?x.l<y.l:(pos[x.r]!=pos[y.r]?x.r<y.r:x.t<y.t);
}

3.移动区间,获得答案

for(int i=1,l=1,r=0,t=0;i<=cnt_q;i++)
{
	while(l>q[i].l)add(--l);
	while(r<q[i].r)add(++r);
	while(l<q[i].l)del(l++);
	while(r>q[i].r)del(r--);
	while(t<q[i].t)modify(i,++t);
	while(t>q[i].t)modify(i,t--);
	ans[q[i].id]=sum;
}

4.带修与普通的区别

modify()函数是带修莫队特有的。

void modify(int x,int t)
{
	if(u[t].id>=q[x].l&&u[t].id<=q[x].r)
	{
		del(u[t].id);
		int tmp=a[u[t].id];
		a[u[t].id]=u[t].val;
		add(u[t].id);
		a[u[t].id]=tmp;
	}
	swap(a[u[t].id],u[t].val);
}

其他与普通莫队没差别。

完整代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5,M=2e3+5;
int n,m,len,num,sum,a[N],L[M],R[M],cnt[N],ans[N],pos[N];
struct query{int l,r,t,id;}q[N];
struct update{int id,val;}u[N];
bool cmp(query x,query y)
{
	return pos[x.l]!=pos[y.l]?x.l<y.l:(pos[x.r]!=pos[y.r]?x.r<y.r:x.t<y.t);
}
void init()
{
	len=pow(n,2.0/3.0);
	num=n/len;
	if(n%len!=0)num++;
	for(int i=1;i<=num;i++)
	{
		L[i]=(i-1)*len+1;
		R[i]=i*len;
	}
	R[num]=n;
	for(int i=1;i<=num;i++)
	{
		for(int j=L[i];j<=R[i];j++)
		{
			pos[j]=i;
		}
	}
}
void add(int x)
{
	cnt[a[x]]++;
	if(cnt[a[x]]==1)++sum;
}
void del(int x)
{
	cnt[a[x]]--;
	if(!cnt[a[x]])--sum;
}
void modify(int x,int t)
{
	if(u[t].id>=q[x].l&&u[t].id<=q[x].r)
	{
		del(u[t].id);
		int tmp=a[u[t].id];
		a[u[t].id]=u[t].val;
		add(u[t].id);
		a[u[t].id]=tmp;
	}
	swap(a[u[t].id],u[t].val);
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	init();
	char opt;
	int cnt_q=0,cnt_r=0;
	for(int i=1;i<=m;i++)
	{
		cin>>opt;
		if(opt=='Q')
		{
			cnt_q++;
			cin>>q[cnt_q].l>>q[cnt_q].r;
			q[cnt_q].t=cnt_r;
			q[cnt_q].id=cnt_q;
		}
		if(opt=='R')
		{
			cnt_r++;
			cin>>u[cnt_r].id>>u[cnt_r].val;
		}
	}
	sort(q+1,q+cnt_q+1,cmp);
	for(int i=1,l=1,r=0,t=0;i<=cnt_q;i++)
	{
		while(l>q[i].l)add(--l);
		while(r<q[i].r)add(++r);
		while(l<q[i].l)del(l++);
		while(r>q[i].r)del(r--);
		while(t<q[i].t)modify(i,++t);
		while(t>q[i].t)modify(i,t--);
		ans[q[i].id]=sum;
	}
	for(int i=1;i<=cnt_q;i++)
	{
		cout<<ans[i]<<'\n';
	}
	return 0;
}

与普通莫队的对比

维度普通莫队带修莫队
状态空间 ( l , r ) (l, r) (l,r) 二维 ( l , r , t ) (l, r, t) (l,r,t) 三维
排序关键字 p o s [ l ] pos[l] pos[l], p o s [ r ] pos[r] pos[r] p o s [ l ] pos[l] pos[l], p o s [ r ] pos[r] pos[r], t t t
指针 c u r L curL curL, c u r R curR curR c u r L curL curL, c u r R curR curR, c u r T curT curT
块长 B = n B = \sqrt{n} B=n B = n 2 / 3 B = n^{2/3} B=n2/3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值