bzoj 4241: 历史研究
传说中的回滚莫队。
按左端点所在块为第一关键字,右端点位置为第2关键字排序。
分别把左端点在同一个块中的询问一起处理,从这个块的下一个块的第一个元素开始加,因为右端点单调递增,只有加元素的操作。
然后对于左边的部分,对每个询问暴力加左边的然后考虑答案就好了。
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=1e5+7;
typedef long long LL;
using namespace std;
int n,m,col[N],ls[N],bl[N],kk,sz;
LL ans[N];
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
struct node {
int id,l,r;
friend bool operator <(const node &A,const node &B) {
return bl[A.l]<bl[B.l]||(bl[A.l]==bl[B.l]&&A.r<B.r);
}
}qs[N];
int cnt[N],lz[N];
LL ansnow=-1;
void add(int x,int id) {
if(lz[x]!=id) lz[x]=id,cnt[x]=1;
else cnt[x]++;
ansnow=max(ansnow,(LL)cnt[x]*ls[x]);
}
int tpc[N],vis[N],tot;
LL tpadd(int l,int r,int id) {
tot++;
LL tpans=ansnow;
for(int i=l;i<=r;i++) {
int x=col[i];
if(vis[x]!=tot) vis[x]=tot,tpc[x]=1;
else tpc[x]++;
int y=(lz[x]==id?cnt[x]:0);
tpans=max(tpans,(LL)(tpc[x]+y)*ls[x]);
}
return tpans;
}
void solve() {
int nl,nr;
for(int i=1;i<=m;i++) {
if(i==1||bl[qs[i].l]!=bl[qs[i-1].l]) //if(bl[qs[i].l]!=bl[qs[i-1].l])
nl=nr=(bl[qs[i].l]+1)*kk,ansnow=-1;
while(nr<qs[i].r) add(col[++nr],bl[qs[i].l]);
ans[qs[i].id]=tpadd(qs[i].l,min(nl,qs[i].r),bl[qs[i].l]);
}
}
int main() {
read(n); read(m);
kk=sqrt(n); kk=max(kk,1);
for(int i=1;i<=n;i++) {
read(col[i]);
ls[i]=col[i];
bl[i]=(i-1)/kk;
}
sort(ls+1,ls+n+1);
sz=unique(ls+1,ls+n+1)-(ls+1);
for(int i=1;i<=n;i++)
col[i]=lower_bound(ls+1,ls+sz+1,col[i])-ls;
for(int i=1;i<=m;i++) {
read(qs[i].l);
read(qs[i].r);
qs[i].id=i;
}
sort(qs+1,qs+m+1);
solve();
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
/*
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
*/