原题链接:
https://www.luogu.org/problem/AT1219
https://www.lydsy.com/JudgeOnline/problem.php?id=4241
人话题面: 给定一个长为n的序列,q次询问区间\([l,r]\)上【出现的数字a×它的出现次数\(cnt_a\)】的最大值
既然是如此熟悉的,查询区间上不同的数的个数,肯定会先想到用莫队来维护最大值,但是原来的莫队不完全适用了,维护最大值添加起来很容易,但是没办法删除(如果你超级强说不定可以但我不会),我能想到的只有\(O(n)\)暴力,显然不行。所以就有了:回滚莫队
我们都很清楚莫队对询问区间的排序方法带来的一种性质:左端点在同一个分块中的所有查询区间右端点单调递增。所以对于左端点在同一个块中的每个区间,我们都可以\(O(n)\)解决所有的右端点,并且由于右端点的单调递增,我们不需要回头删除数据。若分块时以\(\sqrt n\)为块的大小,那么一共有\(\sqrt n\)个块,所以复杂度为\(O(n\sqrt n)\)。【由于左端点在一个块中是无序的,所以我们可以将一个块内的左端点统一记为块的右端,每次查询到一个区间,就把左区间暴力移动到该查询区间的左端点,最坏移动\(\sqrt n\)次,有n个左端点,复杂度也为\(O(n\sqrt n)\)】
如果左右端点都在同一个分块中,那么显然这里暴力统计的最坏复杂度也只是\(O(\sqrt n)\),所以直接暴力。
综上我们可以看到,回滚莫队的总复杂度也还是\(O(n\sqrt n)\)【可以这样想,回滚莫队就是比普通莫队多了个右端点的\(O(n\sqrt n)\)的左移而已】。
这样子的复杂度过这一道题没有问题,莫队万岁!!!
代码:
把这个代码调对可是不容易呢!!!因为ATCoder的数据点太多了。洛谷RemoteJudge太慢了,建议左转BZOJ!
#include<cmath> #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int M=100100; const int N=5050; int n,m; int a[M],b[M],c[M],be[M],cnt[M],cunt[M],br[M]; ll ans[M]; struct ques{ int l,r,id; }q[M]; inline int Read(){ int x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x; } inline bool CMP(ques a,ques b){ return (be[a.l]^be[b.l])?be[a.l]<be[b.l]:a.r<b.r; } int main(){ n=Read(),m=Read(); int sq=sqrt(n),up=ceil((double)n/sq); for(int i=1;i<=up;i++){ br[i]=i*sq; for(int j=(i-1)*sq+1;j<=br[i];j++) be[j]=i; } br[up]=n; for(int i=1;i<=n;i++) a[i]=b[i]=Read(); sort(a+1,a+n+1); int tot=unique(a+1,a+n+1)-a-1; for(int i=1;i<=n;i++) c[i]=lower_bound(a+1,a+tot+1,b[i])-a; 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); int i=1; for(int k=0;k<=up;k++){ int l=br[k]+1,r=br[k]; ll now=0; memset(cnt,0,sizeof(cnt)); for(;be[q[i].l]==k;i++){ int ql=q[i].l,qr=q[i].r; ll tmp; if(be[ql]==be[qr]){ tmp=0; for(int j=ql;j<=qr;j++) cunt[c[j]]=0; for(int j=ql;j<=qr;j++) tmp=max(tmp,1ll*++cunt[c[j]]*b[j]); ans[q[i].id]=tmp; continue; } while(r<qr) now=max(now,1ll*++cnt[c[++r]]*b[r]); tmp=now; while(l>ql) now=max(now,1ll*++cnt[c[--l]]*b[l]); ans[q[i].id]=now; while(l<br[k]+1) --cnt[c[l++]]; now=tmp; } } for(int i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }