回滚莫队
适用范围:
- 离线题,区间伸长容易维护信息,缩短反之。
当然有只删的回滚。
去年暑假贺的板子。
求区间相同的数的最远距离。
\(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;
}
一个排列,问 \(\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;
}