CodeForces - 940F 带单点修改的莫队

带单点修改的莫队

众所周知莫队处理的区间不可以修改,因为要离线排序暴力答案,如果中途有修改的话,排序前后区间不同,答案也就是错的,如果要使答案正确的话还需要将时间统一,例如上次计算的答案在某次修改之后的区间,而这次在修改之前,那么就要回到过去,取消修改的影响,反之相反。

难点就在这啦,按照普通莫队,每次都要统一时间的话时间复杂度会多一维,必然超时。大佬们想出了一种办法去优化,就是用二维莫队,如每次询问 l , r 区间,在 l 分块的基础上再把 r 分块,在多一维的p按从小到大顺序,暴力方法就是上一段说的,大佬们说这个的时间复杂度大约是 n^5/3左右,还是很厉害的。

然后就是一些细节

1.分块的大小为总数2/3次方,一般莫队为1/2。

2.带修改操作需要离散化和记录数量的点会变多,最好数组开原来的两倍。

3.记录被修改的点的前值与后值时,如果有多次在同一位置,记录的两个点应是上一次修改被改为的值与这次修改后的值,而不是a[p]与这次修改后的值。(a数组指给你的区间的值,长度为n的那个)

4.在记录时如改变了a数组的值,需要记录完后改回去,因为一般时间从0开始。

5.排序规则对 l , r 分块排序,对时间正常排。

这些细节都是我惨痛教训。

例题:CodeForces - 940F

题意:给一个长度n数组,a数组记录值,q次操作,两种操作,一种修改a+p位置的值,一种算 l , r 区间的值的个数的mex(mex指,如果有s个相等的数,那s是好的,然后你要输出最小的不好的。如mex=3,说明区间内有相等的数的个数为1的存在,为2的存在,为3的不存在)。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

int n,q,ans[200007],a[200007],b[200007],l,r,z,size,head,maze,ak[200007],num[200007],cnt[200007];
vector<int>lsh,qans;

struct madoka{
	int l;
	int r;
	int p;
}ho[200007],lin;
vector<madoka>ma;

bool cmp(madoka a1,madoka a2){
	if(ak[a1.l]==ak[a2.l]){
		if(ak[a1.r]==ak[a2.r]){
			return a1.p<a2.p;
		}
		return ak[a1.r]<ak[a2.r];
	}
	return ak[a1.l]<ak[a2.l];
}

void dis(){
	sort(lsh.begin(),lsh.end());
	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(lsh.begin(),lsh.end(),a[i])-lsh.begin()+1;
	}
	for(int i=1;i<=q;i++){
		if(ho[i].p!=0){
			ho[i].l=lower_bound(lsh.begin(),lsh.end(),ho[i].l)-lsh.begin()+1;
			ho[i].r=lower_bound(lsh.begin(),lsh.end(),ho[i].r)-lsh.begin()+1;
		}
	}
}

void go(){
	l=1,r=1;
	int p=0;
	cnt[num[a[r]]]--;
	num[a[r]]++;
	cnt[num[a[r]]]++;
	for(int i=0;i<ma.size();i++){
		while(r<ma[i].r){
			r++;
			cnt[num[a[r]]]--;
			num[a[r]]++;
			cnt[num[a[r]]]++;
		}
		while(r>ma[i].r){
			cnt[num[a[r]]]--;
			num[a[r]]--;
			cnt[num[a[r]]]++;
			r--;
		}
		while(l<ma[i].l){
			cnt[num[a[l]]]--;
			num[a[l]]--;
			cnt[num[a[l]]]++;
			l++;
		}
		while(l>ma[i].l){
			l--;
			cnt[num[a[l]]]--;
			num[a[l]]++;
			cnt[num[a[l]]]++;
		}
		while(p<ma[i].p){
			p++;
			if(ho[p].p==0)continue;
			if(ma[i].l<=ho[p].p&&ma[i].r>=ho[p].p){
				cnt[num[ho[p].l]]--;
				num[ho[p].l]--;
				cnt[num[ho[p].l]]++;
				cnt[num[ho[p].r]]--;
				num[ho[p].r]++;
				cnt[num[ho[p].r]]++;
			}
			a[ho[p].p]=ho[p].r;
		}
		while(p>ma[i].p){
			if(ho[p].p==0){
				p--;
				continue;
			}
			if(ma[i].l<=ho[p].p&&ma[i].r>=ho[p].p){
				cnt[num[ho[p].l]]--;
				num[ho[p].l]++;
				cnt[num[ho[p].l]]++;
				cnt[num[ho[p].r]]--;
				num[ho[p].r]--;
				cnt[num[ho[p].r]]++;
			}
			a[ho[p].p]=ho[p].l;
			p--;
		}
		for(int j=1;j<=n;j++){
			if(cnt[j]==0){
				ans[ma[i].p]=j;
				break;
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&q);
	size=(int)pow(n,2.0/3.0);
	maze=ceil((double)n/size);
	for(int i=1;i<=maze;i++){
		for(int j=head;j<head+size&&j<=n;j++){
			ak[j]=i;
		}
		head+=size;
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=a[i];
		lsh.push_back(a[i]);
	}
	for(int i=1;i<=q;i++){
		scanf("%d%d%d",&z,&l,&r);
		if(z==1){
			lin.l=l;
			lin.r=r;
			lin.p=i;
			ma.push_back(lin);
			qans.push_back(i);
		}
		else{
			lin.l=a[l];
			lin.r=r;
			lin.p=l;
			ho[i]=lin;
			a[l]=r;
			lsh.push_back(r);
		}
	}
	for(int i=1;i<=n;i++)a[i]=b[i];
	dis();
	sort(ma.begin(),ma.end(),cmp);
	go();
	for(int i=0;i<qans.size();i++){
		printf("%d\n",ans[qans[i]]);
	}
}
posted @ 2020-06-27 22:19  ccsu_madoka  阅读(183)  评论(1编辑  收藏  举报