题目大意
给出长度为n的序列a,有一些询问a[l,r]中出现过的数字乘以出现的次数的最大值。
解题思路
可以考虑使用莫队,但是我们发现加一个数简单,删一个数就有点难了,可以使用线段树维护,然而这会tle。我们可以尽量使用加,按l所在区间降序为第一关键字,r升序为第二关键字。维护一个桶,先不考虑l所在区间的部分,考虑完整的块和右边,可以轻松维护。再考虑l所在区间的部分,先存下当前的答案,暴力加上l所在区间的部分,最多n√,总复杂度nn√,再消除标记,还原答案即可。
code
using namespace std;
int const maxn=100000,maxm=100000,inf=2147483647;
LL n,m,size,a[maxn+10],b[maxn+10],c[maxn+10],d[maxn+10],cnt[maxn+10],ans[maxn+10];
struct rec{
LL l,r,p;
};
rec q[maxn+10];
bool cmp(LL i,LL j){
return a[i]<a[j];
}
bool cmp2(rec i,rec j){
return (i.l/size>j.l/size)||((i.l/size==j.l/size)&&(i.r<j.r));
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%lld%lld",&n,&m);size=sqrt(n);
fo(i,1,n)scanf("%lld",&a[i]),c[i]=i;
sort(c+1,c+n+1,cmp);
fo(i,1,n){
if(a[c[i]]!=d[a[c[i-1]]])d[++d[0]]=a[c[i]];
a[c[i]]=d[0];
}
fo(i,1,m)
scanf("%lld%lld",&q[i].l,&q[i].r),q[i].p=i;
sort(q+1,q+n+1,cmp2);
fo(i,(q[1].l/size+1)*size+1,q[1].r){
cnt[a[i]]++;
ans[0]=max(ans[0],cnt[a[i]]*d[a[i]]);
}
LL tmp=ans[0];
fo(j,q[1].l,(q[1].l/size+1)*size){
cnt[a[j]]++;
ans[0]=max(ans[0],cnt[a[j]]*d[a[j]]);
}
ans[q[1].p]=ans[0];
fo(j,q[1].l,(q[1].l/size+1)*size)
cnt[a[j]]--;
ans[0]=tmp;
fo(i,2,m){
if(q[i].l/size!=q[i-1].l/size){
fo(j,1,n)cnt[j]=0;ans[0]=0;
fo(j,(q[i].l/size+1)*size+1,q[i].r){
cnt[a[j]]++;
ans[0]=max(ans[0],cnt[a[j]]*d[a[j]]);
}
LL tmp=ans[0];
fo(j,q[i].l,min(q[i].r,(q[i].l/size+1)*size)){
cnt[a[j]]++;
ans[0]=max(ans[0],cnt[a[j]]*d[a[j]]);
}
ans[q[i].p]=ans[0];
fo(j,q[i].l,min(q[i].r,(q[i].l/size+1)*size))
cnt[a[j]]--;
ans[0]=tmp;
}else{
fo(j,q[i-1].r+1,q[i].r){
cnt[a[j]]++;
ans[0]=max(ans[0],cnt[a[j]]*d[a[j]]);
}
LL tmp=ans[0];
fo(j,q[i].l,min(q[i].r,(q[i].l/size+1)*size)){
cnt[a[j]]++;
ans[0]=max(ans[0],cnt[a[j]]*d[a[j]]);
}
ans[q[i].p]=ans[0];
fo(j,q[i].l,min(q[i].r,(q[i].l/size+1)*size))
cnt[a[j]]--;
ans[0]=tmp;
}
}
fo(i,1,m)printf("%lld\n",ans[i]);
return 0;
}