BZOJ4241 历史研究(莫队)
如果分块的话与区间众数没有本质区别。这里考虑莫队。
显然莫队时的删除可以用堆维护,但多了一个log不太跑得过。
有一种叫回滚莫队的trick,可以将问题变为只有加入操作。按莫队时分的块依次处理,一块中左端点的差不超过√n,右端点单调递增。首先将右端点也在该块中的询问暴力处理。然后令指针l在下一块开头,指针r在这一块结尾。暴力扩展右端点移动指针r,到达询问点时,移动指针l以回答询问,但不让指针l的移动对之后的询问产生影响,即回滚。这样就可以处理删除了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 #define ll long long int n,m,a[N],b[N],cnt[N]; ll ans[N]; struct data { int x,y,k,i; bool operator <(const data&a) const { return k<a.k||k==a.k&&y<a.y; } }q[N]; int main() { #ifndef ONLINE_JUDGE freopen("bzoj4241.in","r",stdin); freopen("bzoj4241.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); for (int i=1;i<=n;i++) b[i]=a[i]=read(); sort(b+1,b+n+1); int t=unique(b+1,b+n+1)-b-1; for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+t+1,a[i])-b; int block=sqrt(n); for (int i=1;i<=m;i++) q[i].x=read(),q[i].y=read(),q[i].k=q[i].x/block,q[i].i=i; sort(q+1,q+m+1); for (int i=1;i<=m;i++) { int t=i;while (t<m&&q[t+1].k==q[i].k) t++; while (i<=t&&q[i].y<(q[i].k+1)*block) { for (int j=q[i].x;j<=q[i].y;j++) { cnt[a[j]]++; ans[q[i].i]=max(ans[q[i].i],1ll*cnt[a[j]]*b[a[j]]); } for (int j=q[i].x;j<=q[i].y;j++) cnt[a[j]]--; i++; } int r=(q[i].k+1)*block-1;ll v=0; for (int j=i;j<=t;j++) { while (r<q[j].y) { cnt[a[++r]]++; v=max(v,1ll*cnt[a[r]]*b[a[r]]); } ans[q[j].i]=v; for (int k=(q[i].k+1)*block-1;k>=q[j].x;k--) { cnt[a[k]]++; ans[q[j].i]=max(ans[q[j].i],1ll*cnt[a[k]]*b[a[k]]); } for (int k=(q[i].k+1)*block-1;k>=q[j].x;k--) cnt[a[k]]--; } memset(cnt,0,sizeof(cnt)); i=t; } for (int i=1;i<=m;i++) printf(LL,ans[i]); return 0; }