题链:https://www.luogu.com.cn/problem/AT1219
思路:
对于普通莫队,我们一般考虑添加和删除只是顺序变一下,但有的题删除操作根本没办法维护需要的东西。
这时,不删除莫队就可以大显神威了,删除太难了,那我们不删除,只添加就好了。
现在考虑怎么不删除,我们排序时,对于左端点在同一个块内的询问他们的r是从小到大有序的,那么我们可以利用这个特点,一个块一个块的计算询问区间。
对于左端点在同一个块内的询问:
首先考虑,如果询问区间的左右端点(ql、qr)都在一个块内,那我们直接暴力计算,复杂度为,最多m个询问区间的左右端点(ql、qr)都在一个块内,复杂度为
。
否则,我们可以保持r有序,那么对于左端点一个块内的询问,r最坏从头到尾遍历一遍为,总共
块,复杂度也为
。
对于每个询问,我们现在只要移动l即可,l最多移动次,最多m个询问区间,
。
总体复杂度。
对于块i中的询问,将l和r初值置为l=br[i]+1,r=br[i]即可。
注意,对于l移动时,带来的修改只对当前询问有关,所以改了要再改回去,有时还要维护两组状态。
注意,这里还有个块0需要带着。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
int n,m,a[N];
set<int> s;
set<int>::iterator it;
unordered_map<int,int> mp;
struct node{
int l,r,id;
}q[N];
int bl[N],br[N],bnum,block,be[N];
bool cmp(node a,node b){
return (be[a.l]^be[b.l]) ? be[a.l]<be[b.l] : a.r<b.r;
}
ll ans[N];
int cnt[N],cnt1[N];
int main(void){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
s.insert(a[i]);
}
int Id=0;
for(it=s.begin();it!=s.end();it++)
mp[(*it)]=++Id;
block=ceil(sqrt(1.0*n));
bnum=ceil(1.0*n/block);
for(int i=1;i<=bnum;i++){
bl[i]=br[i-1]+1,br[i]=br[i-1]+block;
for(int j=bl[i];j<=br[i];j++)
be[j]=i;
}
for(int i=1;i<=m;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+m,cmp);
int pos=1;
ll sum;
for(int i=0;i<=bnum;i++){
sum=0;
memset(cnt,0,sizeof cnt);
int l=br[i]+1,r=br[i];
for(;be[q[pos].l]==i;pos++){
int ql=q[pos].l,qr=q[pos].r;
if(be[ql]==be[qr]){
ll temp=0;
for(int j=ql;j<=qr;j++) cnt1[mp[a[j]]]=0;
for(int j=ql;j<=qr;j++){
++cnt1[mp[a[j]]];
temp=max(1LL*a[j]*cnt1[mp[a[j]]],temp) ;
}
ans[q[pos].id]=temp;
continue;
}
while(r<qr){
++r;
++cnt[mp[a[r]]];
sum=max(sum,1LL*a[r]*cnt[mp[a[r]]]);
}
ll temp=sum;//后面的修改只对这次询问有用
while(l>ql){
--l;
++cnt[mp[a[l]]];
sum=max(sum,1LL*a[l]*cnt[mp[a[l]]]);
}
ans[q[pos].id]=sum;
while(l<br[i]+1){//改回去
--cnt[mp[a[l]]];
l++;
}
sum=temp;//改回去
}
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}