【校内模拟】music(多重分块)(非常规大小分块)

简要题意不放了,强制在线主席树SB题一道,卡空间需要用分块做到 O(n)O(n) 空间。


题解:

由于发下来的题解几乎就是在口胡,我跑去UOJ群问了一下这道题的 O(n)O(n) 空间做法,幸运地得到了myh的教育,下面的做法就来自于myh神仙。

先考虑对时间进行分块,块大小为 SS,考虑把每块中会进行修改的位置拿出来,然后把原序列中所有 n/Sn/S 的倍数的位置拿出来,以这些位置为端点,把序列分成 O(S)O(S) 块,每块的大小不超过 O(n/S)O(n/S)

这样做的好处就是,在考虑这个时间区间内的修改时,每一块内部的变化量任何时候都是相等的,这块内部最小值所在位置就只可能是进行修改前的最小值所在位置,并且任何一个散点到块端点的距离不超过 O(n/S)O(n/S)

于是一次询问需要考虑的就是 O(S)O(S) 次修改对 O(S)O(S) 个整块的影响和 O(n/S)O(n/S) 个散点的影响。

首先考虑整块修改,我们可以预处理出每一块原来的最小值,然后把修改打到差分数组上,再 O(S)O(S) 将所有块扫一遍,利用我们需要的块更新答案。

对于散点,我们考虑处理出每个块块头在当前时间块之前的值,然后直接 O(S)O(S) 操作零散的修改算出当前块头的值。容易注意到两个相邻位置的差是 O(1)O(1) 的,于是 O(n/S)O(n/S) 把需要的散点计算出来即可。

S=O(n)S=O(\sqrt n) ,复杂度为 O(nn)O(n\sqrt n),由于处理散点的常数有点大,SS 需要稍微缩小一点。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cin;
using std::cerr;
using std::cout;

cs int N=2e5+7;
cs int S=250,M=N/S+7;

int n,m,len;

int a[N],t[N];
int id[N],rk[N];
int b[N],bn;

int *mn[N],*bg[N];
int bl[N],*nd[N];

int dif[N];
int tmp[N],ct;

void Main(){
	scanf("%d%d",&n,&m);len=n-m+1;
	for(int re i=0;i<n;++i)
		cin>>a[i],b[i]=a[i],id[i]=i;
	std::sort(b,b+n);bn=std::unique(b,b+n)-b;
	std::sort(id,id+n,
		[](int i,int j){return a[i]<a[j];});
	for(int re i=0;i<n;++i)rk[id[i]]=i;
	for(int re i=0;i<n;++i)
		t[std::lower_bound(b,b+bn,a[id[i]])-b]=i;
	for(int re i=0;i<n;i+=S){
		for(int re j=0;j<len;j+=len/S+1)tmp[ct++]=j;
		for(int re j=i;j<i+S&&j<n;++j){
			tmp[ct++]=std::min(id[j]+1,len);
			tmp[ct++]=std::max(0,id[j]-m+1);
		}tmp[ct++]=len;std::sort(tmp,tmp+ct);
		ct=std::unique(tmp,tmp+ct)-tmp;
		int bid=i/S;bl[bid]=ct;
		nd[bid]=new int[ct];
		mn[bid]=new int[ct];bg[bid]=new int[ct];
		memcpy(nd[bid],tmp,sizeof(int)*ct);
		for(int j=0,val=0,nw=0;j<=len;++j){
			val+=dif[j];
			if(nw<ct&&j==nd[bid][nw]){
				bg[bid][nw]=val;mn[bid][nw]=val;++nw;
			}else mn[bid][nw-1]=std::min(mn[bid][nw-1],val);
		}
		for(int re j=i;j<i+S&&j<n;++j){
			--dif[std::min(id[j]+1,len)];
			++dif[std::max(0,id[j]-m+1)];
		}ct=0;
	}memset(dif,0,sizeof dif);
	int Q,ans=0;scanf("%d",&Q);
	while(Q--){
		int l,r,x;scanf("%d%d%d",&l,&r,&x);--l,--r;
		x^=ans;int T=std::lower_bound(b,b+bn,x)-b;
		if(!T){cout<<(ans=0)<<"\n";continue;}
		T=t[T-1];int pl,pr,bid=T/S;ans=1e9;
		pl=std::upper_bound(nd[bid],nd[bid]+bl[bid],l)-nd[bid]-1;
		pr=std::upper_bound(nd[bid],nd[bid]+bl[bid],r)-nd[bid]-1;
		if(pl==pr){
			int val=bg[bid][pl];
			for(int re i=bid*S;i<=T;++i)
				if(id[i]-m+1<=nd[bid][pl]&&nd[bid][pl]<=id[i])
					++val;
			for(int re i=nd[bid][pl];i<=r;++i){
				if(l<=i)ans=std::min(ans,val);
				if(rk[i]<=T)--val;
				if(i+m<n&&rk[i+m]<=T)++val;
			}
		}else {
			int val=bg[bid][pl];
			for(int re i=bid*S;i<=T;++i)
				if(id[i]-m+1<=nd[bid][pl]&&nd[bid][pl]<=id[i])
					++val;
			for(int re i=nd[bid][pl];i<nd[bid][pl+1];++i){
				if(l<=i)ans=std::min(ans,val);
				if(rk[i]<=T)--val;
				if(i+m<n&&rk[i+m]<=T)++val;
			}val=bg[bid][pr];
			for(int re i=bid*S;i<=T;++i)
				if(id[i]-m+1<=nd[bid][pr]&&nd[bid][pr]<=id[i])
					++val;
			for(int re i=nd[bid][pr];i<=r;++i){
				ans=std::min(ans,val);
				if(rk[i]<=T)--val;
				if(i+m<n&&rk[i+m]<=T)++val;
			}for(int re i=bid*S;i<=T;++i){
				--dif[std::min(id[i]+1,len)];
				++dif[std::max(0,id[i]-m+1)];
			}val=0;
			for(int re i=0;i<pr;++i){
				val+=dif[nd[bid][i]];
				if(i>pl)ans=std::min(ans,val+mn[bid][i]);
			}
			for(int re i=bid*S;i<=T;++i){
				dif[std::min(id[i]+1,len)]=0;
				dif[std::max(0,id[i]-m+1)]=0;
			}
		}cout<<ans<<"\n";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("music.in","r",stdin);
	freopen("music.out","w",stdout);
#endif
}signed main(){file();Main();return 0;} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章