【题解/学习笔记】回滚莫队
回滚莫队是对付一类操作难以撤回的莫队做法。
从例题开始。
题目要求区间中相同数的最大坐标差,显然地这个东西撤回是很难的。
那么考虑回滚莫队是如何做这件事情的:
首先我们把询问分块,然后将询问的左端点所在块作为第一关键字,把右端点置为第二关键字。
经过上述排序后我们发现,左端点在同一个块内的询问,右端点都是递增的。也就是说,我们在处理左端点在同一个块内的询问的时候,右端点可以保证不会进行撤回操作。
那么考虑左端点如何做?
由于我们发现,区间长度是 \(O(\sqrt{n})\) 的,所以我们考虑一种暴力的做法:每次把左指针 \(l\) 暴力拉回去,重新扫。
那么我们面临的问题就是如何把指针拉回去。我们考虑,在增加指针的时候,记录下指针的变化,也就是加入这个数的前置状态,然后我们按顺序把这个前置状态还原回去即可。
那么其具体流程如下:
-
对询问排序
-
- 若当前询问的左端点块编号和上一次询问的不同
-
-
- 令左指针为当前左端点块右端点 \(+1,\) 右指针为当前左端点块右端点。
-
-
- (注意要先进行上面的判断,保证指针移动到位) 如果当前询问的左右端点在同一个块中,直接暴力解决。(因为这种询问按照上述初始化是计算不到的)
-
- 若不在同一个块里面,就先让右端点移过去,再移动左端点,计算答案之后,将状态还原。
容易发现复杂度是对的:因为右端点的复杂度正常,而每次询问左端点的移动次数都不超过 \(O(\sqrt{n})\)
于是题目得解。
例题:AT1219 歴史の研究
同上述流程维护莫队,维护一个 cnt
记录次数即可,要记录的前驱状态就是答案和颜色。
模板代码:
#include <bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
const int inf = (1 << 30);
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
char buf[1<<21],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
if(x==0){*O++='0';return;}
if(x<0)*O++='-',x=-x;ptop=0;
while(x)pst[++ptop]=x%10,x/=10;
while(ptop)*O++=pst[ptop--]+'0';
if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
inline void write(int x) {
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10);
pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
int res = 1;
while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e5 + 10;
namespace Refined_heart{
int bl[N],n,a[N],b[N],blen,m,siz;
struct Node{int lv,rv,pans,vl;Node(int x_=0,int y_=0,int z_=0,int ww_=0){lv=x_,rv=y_,pans=z_,vl=ww_;}};
vector<Node>v;
struct QR{
int l,r,id;
QR(int xx=0,int yy=0,int zz=0){
l=xx;
r=yy;
id=zz;
}
}q[N];
inline bool cmp(QR x,QR y){
if(bl[x.l]==bl[y.l])return x.r<y.r;
return bl[x.l]<bl[y.l];
}
inline int getpos(int x){return lower_bound(b+1,b+blen+1,x)-b;}
int rp[N],lp[N],rans[N];
int Ans=0;
void add(int now){
v.pb(Node(lp[a[now]],rp[a[now]],Ans,a[now]));
cmin(lp[a[now]],now);
cmax(rp[a[now]],now);
cmax(Ans,rp[a[now]]-lp[a[now]]);
}
int cl[N],cr[N];
void calc(int l,int r,int nop){
int ans=0;
for(int i=l;i<=r;++i){
cmax(cr[a[i]],i);
cmin(cl[a[i]],i);
cmax(ans,cr[a[i]]-cl[a[i]]);
}
for(int i=l;i<=r;++i)cr[a[i]]=0,cl[a[i]]=inf;
rans[nop]=ans;
}
void solve(){
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=n;++i)b[i]=a[i];
sort(b+1,b+n+1);
blen=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)a[i]=getpos(a[i]);
m=read();
for(int i=1;i<=m;++i)q[i].l=read(),q[i].r=read(),q[i].id=i;
siz=sqrt(n);
for(int i=1;i<=n;++i)bl[i]=((i-1)/siz)+1;
sort(q+1,q+m+1,cmp);
memset(lp,0x3f,sizeof lp);
memset(cl,0x3f,sizeof cl);
int l=1,r=0;bl[0]=-1;
for(int i=1;i<=m;++i){
int ql=q[i].l;
int qr=q[i].r;
if(bl[ql]!=bl[q[i-1].l]){
int pos=bl[ql]*siz;
r=pos;l=pos+1;
while(!v.empty()){
Node now=v.back();
v.pop_back();
int val=now.vl;
lp[val]=inf,rp[val]=0;
}
Ans=0;
}
if(bl[ql]==bl[qr]){
calc(ql,qr,q[i].id);
continue;
}
while(r<qr)add(++r);
while(l>ql)add(--l);
rans[q[i].id]=Ans;
while(l<bl[ql]*siz+1){
Node now=v.back();
v.pop_back();
int val=now.vl;
++l;
lp[val]=now.lv;
rp[val]=now.rv;
Ans=now.pans;
}
}
for(int i=1;i<=m;++i)Fwrite(rans[i]),*O++='\n';
Fprint();
}
}
signed main(){
freopen("in.txt","r",stdin);
freopen("My.out","w",stdout);
Refined_heart::solve();
return 0;
}
例题代码:
#include <bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
const int inf = (1 << 30);
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
if(x==0){*O++='0';return;}
if(x<0)*O++='-',x=-x;ptop=0;
while(x)pst[++ptop]=x%10,x/=10;
while(ptop)*O++=pst[ptop--]+'0';
if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
inline void write(int x) {
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10);
pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
int res = 1;
while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e5 + 10;
namespace Refined_heart{
int n,a[N],m,siz,bl[N];
int b[N],blen;
struct QR{
int l,r,id;
}q[N];
struct Node{
int col,pans;
Node(int x_=0,int y_=0){
col=x_;
pans=y_;
}
};
vector<Node>rub;
int Ans=0,rans[N];
inline int getpos(int x){return lower_bound(b+1,b+blen+1,x)-b;}
inline bool cmp(QR x,QR y){
if(bl[x.l]==bl[y.l])return x.r<y.r;
return bl[x.l]<bl[y.l];
}
int ct[N],cnt[N];
void solve_(int l,int r,int op){
int ans=0;
for(int i=l;i<=r;++i)ct[a[i]]++,cmax(ans,b[a[i]]*ct[a[i]]);
for(int i=l;i<=r;++i)ct[a[i]]=0;
rans[op]=ans;
}
void add(int now){
Node pt=Node(a[now],Ans);rub.pb(pt);
cnt[a[now]]++;cmax(Ans,b[a[now]]*cnt[a[now]]);
}
void solve(){
n=read();m=read();
for(int i=1;i<=n;++i)a[i]=read();
siz=sqrt(n);
for(int i=1;i<=n;++i)bl[i]=(i-1)/siz+1;
for(int i=1;i<=m;++i){
q[i].l=read();
q[i].r=read();
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
for(int i=1;i<=n;++i)b[i]=a[i];
sort(b+1,b+n+1);
blen=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)a[i]=getpos(a[i]);
int l=1,r=0;
for(int i=1;i<=m;++i){
int ql=q[i].l;
int qr=q[i].r;
if(bl[ql]!=bl[q[i-1].l]){
int pos=bl[ql]*siz;
l=pos+1;
r=pos;
while(!rub.empty()){
Node v=rub.back();
rub.pop_back();
cnt[v.col]=0;
}
Ans=0;
}
if(bl[ql]==bl[qr]){
solve_(ql,qr,q[i].id);
continue;
}
while(r<qr)add(++r);
while(l>ql)add(--l);
rans[q[i].id]=Ans;
while(l<bl[ql]*siz+1){
Node v=rub.back();
rub.pop_back();
++l;
cnt[v.col]--;
Ans=v.pans;
}
}
for(int i=1;i<=m;++i)write(rans[i]),pc('\n');
}
}
signed main(){
freopen("in.txt","r",stdin);
Refined_heart::solve();
return 0;
}