回滚莫队

适用范围:

  • 离线题,区间伸长容易维护信息,缩短反之。

当然有只删的回滚。

去年暑假贺的板子。

P5906 【模板】回滚莫队&不删除莫队

求区间相同的数的最远距离。

\(1\le n,m\le 2\times10^5\).

将询问按普通莫队排序,故左端点在一个块且右端点递增。

每次令 \(l=R_i+1,r=R_i\) 表示本块的初始空区间。

左右端点在同块内直接 \(O(\sqrt{n})\) 暴力。

现在所有需要处理的询问都有 \(q\lbrack j\rbrack.r>R_i\).

处理询问时将 \(r\) 移动至询问的右端点并保存信息。

\(l\) 移动到左端点求出答案。

\(l\rightarrow R_i+1\),利用之前的信息进行恢复。

时间复杂度 \(O(n\sqrt{n})\).

比较丑陋的代码。

#include<bits/stdc++.h>
#define ll long long
#define N 200010
using namespace std;
int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x;
}
int n,m,maxn,tot;
int a[N],tp[N],b[N];
int ans[N];
struct query{
	int l,r,id;
	bool operator<(const query &x)const{
		return (b[l]^b[x.l])?l<x.l:r<x.r;
	}
}q[N];
queue<int>buc;
int lst[N],st[N];
int calc(int l,int r){
	int ret=0;
	for(int i=l;i<=r;i++)tp[a[i]]=0;
	for(int i=l;i<=r;i++){
		if(!tp[a[i]])tp[a[i]]=i;
		else ret=max(ret,i-tp[a[i]]);
	}
	return ret;
}
int main(){
	n=read(),maxn=(int)sqrt(n);
	for(int i=1;i<=n;i++){
		a[i]=tp[i]=read();
		b[i]=(i-1)/maxn+1;
	}
	tot=b[n];
	sort(tp+1,tp+1+n);
	int len=unique(tp+1,tp+1+n)-(tp+1);
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(tp+1,tp+1+len,a[i])-tp;
	m=read();
	for(int i=1;i<=m;i++)
		q[i]=(query){read(),read(),i};
	sort(q+1,q+1+m);
	int br,l,r,res,t;
	for(int i=1,j=1;j<=tot;j++){
		br=min(n,j*maxn),l=br+1,r=br,res=0;
		for(;b[q[i].l]==j;i++){
			if(b[q[i].r]==j){
				ans[q[i].id]=calc(q[i].l,q[i].r);
				continue;
			}
			while(r<q[i].r){
				lst[a[++r]]=r;
				if(!st[a[r]])
					st[a[r]]=r,buc.push(a[r]);
				res=max(res,r-st[a[r]]);
			}
			int temp=res;
			while(l>q[i].l){
				l--;
				if(lst[a[l]])res=max(res,lst[a[l]]-l);
				else lst[a[l]]=l;
			}
			ans[q[i].id]=res;
			while(l<=br){
				if(lst[a[l]]==l)lst[a[l]]=0;
				l++;
			}
			res=temp;
		}
		while(!buc.empty()){
			lst[buc.front()]=st[buc.front()]=0;
			buc.pop();
		}
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	
	return 0;
}

歴史の研究

\[\max_A{A\times T_A} \]

\(T_A\)\(A\)\(\lbrack L,R\rbrack\) 中的出现次数。

\(1\le n,m\le 10^5\)\(1\le\forall A\le 10^9\).

其实这个题是相对更简单的。

#include<bits/stdc++.h>
#define ll long long
#define N 100010
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,maxn;
int a[N],tp[N],b[N],tot;
ll ans[N];
struct Q{
	int l,r,id;
	Q(int _l=0,int _r=0,int _id=0):l(_l),r(_r),id(_id){}
	bool operator<(const Q &x)const{
		return (b[l]!=b[x.l])?l<x.l:r<x.r;
	}
}q[N];
int cnt[N],force[N];
ll brute(int l,int r){
	ll ret=0;
	for(int i=l;i<=r;i++)
		ret=max(ret,1ll*tp[a[i]]*(++force[a[i]]));
	for(int i=l;i<=r;i++)
		force[a[i]]=0;
	return ret;
}
int main(){
	n=read(),m=read(),maxn=sqrt(n);
	for(int i=1;i<=n;i++){
		a[i]=tp[i]=read();
		b[i]=(i-1)/maxn+1;
	}
	tot=b[n],sort(tp+1,tp+1+n);
	int len=unique(tp+1,tp+1+n)-(tp+1);
	for(int i=1;i<=n;i++)
		a[i]=lower_bound(tp+1,tp+1+len,a[i])-tp;
	for(int i=1,l,r;i<=m;i++){
		l=read(),r=read();
		q[i]=Q(l,r,i);
	}
	sort(q+1,q+1+m);
	int br,l,r;ll cur;
	for(int i=1,j=1;j<=tot;j++){
		br=min(j*maxn,n),l=br+1,r=br,cur=0;
		for(;b[q[i].l]==j;i++){
			if(b[q[i].r]==j){
				ans[q[i].id]=brute(q[i].l,q[i].r);
				continue;
			}
			while(r<q[i].r)
				r++,cur=max(cur,1ll*tp[a[r]]*(++cnt[a[r]]));
			ll pre=cur;
			while(l>q[i].l)
				l--,cur=max(cur,1ll*tp[a[l]]*(++cnt[a[l]]));
			ans[q[i].id]=cur;
			while(l<=br)cnt[a[l++]]--;
			cur=pre;
		}
		for(int i=br;i<=n;i++)cnt[a[i]]=0;
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	
	return 0;
}

P8078 [WC2022] 秃子酋长

一个排列,问 \(\lbrack l,r\rbrack\) 排序后,原本相邻的数的位置的差的绝对值之和。

\(1\le n,m\le 5\times 10^5\)\(\mathbb{TL}=5\rm s\).

暴力做法是莫队加一个 \(\rm set\) 维护前驱后继,时间复杂度 \(O(n\sqrt{n}\log n)\).

考虑只删莫队,可以放到链表上把 \(\log\) 优化掉。

套路是差不多的。不太好写。

#include<bits/stdc++.h>
#define ll long long
#define N 500010
#define L(x) (x-1)*maxn+1
#define R(x) min(x*maxn,n)
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,maxn,b[N],tot;
struct data{
	int x,id;
	data(int _x=0,int _id=0):x(_x),id(_id){}
	bool operator<(const data &r)const{
		return x<r.x;
	}
}a[N];
struct List{
	int pre,nxt,val;
}q[N],tp[N];
int head,tail;
struct Q{
	int l,r,id;
	Q(int _l=0,int _r=0,int _id=0):l(_l),r(_r),id(_id){}
	bool operator<(const Q &x)const{
		return (b[l]!=b[x.l])?l<x.l:r>x.r;
	}
}qr[N];
ll ans[N],cur;
void cpy(List *x,List *y){
	for(int i=1;i<=n;i++)
		x[i]=y[i];
}
void del(int x){
	if(x!=head)cur-=abs(x-q[x].pre);
	if(x!=tail)cur-=abs(x-q[x].nxt);
	if(x!=head&&x!=tail)cur+=abs(q[x].pre-q[x].nxt);
	if(x==head)head=q[x].nxt;
	if(x==tail)tail=q[x].pre;
	q[q[x].pre].nxt=q[x].nxt;
	q[q[x].nxt].pre=q[x].pre;
}
void add(int x){
	q[q[x].pre].nxt=x;
	q[q[x].nxt].pre=x;
}
int main(){
	n=read(),m=read(),maxn=sqrt(n);
	for(int i=1;i<=n;i++){
		a[i]=data(read(),i);
		b[i]=(i-1)/maxn+1;
	}
	sort(a+1,a+1+n),head=a[1].id,tail=a[n].id;
	for(int i=1;i<=n;i++){
		q[a[i].id].pre=a[i-1].id;
		q[a[i].id].nxt=a[i+1].id;
	}
	for(int i=1;i<=n;i++)
		if(i!=head)cur+=abs(i-q[i].pre);
	for(int i=1,l,r;i<=m;i++){
		l=read(),r=read();
		qr[i]=Q(l,r,i);
	}
	sort(qr+1,qr+1+m),tot=b[n];
	int l=1,r=n;
	for(int i=1,j=1,l=1,r=n;j<=tot;j++){
		if(b[qr[i].l]!=j)continue;
		while(l<L(j))del(l++);
		cpy(tp,q);
		ll pre=cur;int hd=head,tl=tail;
		for(;b[qr[i].l]==j;i++){
			while(r>qr[i].r)del(r--);
			ll ppre=cur;int hhd=head,ttl=tail;
			while(l<qr[i].l)del(l++);
			ans[qr[i].id]=cur;
			while(l>L(j))add(--l);
			cur=ppre,head=hhd,tail=ttl;
		}
		cpy(q,tp),cur=pre;
		head=hd,tail=tl,r=n;
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	
	return 0;
}
posted @ 2023-08-06 20:04  SError  阅读(30)  评论(0编辑  收藏  举报