题意
给定一个长度为 n n 的序列,并提出个询问,每次询问要求回答区间 [l,r] [ l , r ] 内所有的权值与其出现次数的积的最大值。
题解
我一看,区间问题,无修改可以离线,上莫队妥妥的,但是仔细一想,当区间扩充时,可以 O(1) O ( 1 ) 的求出新区间答案,但是当区间缩小时,就不能 O(1) O ( 1 ) 做到了,莫队算法的复杂度不能得到保证。
这时候就可以考虑回滚莫队,其思想还是对询问排序,并且对区间的双指针有优化。
普通的莫队算法,初始的
l,r
l
,
r
就是第一个询问的位置的
[L,R]
[
L
,
R
]
,之后对
l,r
l
,
r
就没有人为的修改操作,只让他随着询问变化,在能够
O(1)
O
(
1
)
求出新区间答案的时候,这样的莫队算法保证了复杂度在
n1.5
n
1.5
。
此时,由于不能直接
O(1)
O
(
1
)
求出区间扩张(或收缩)的时候,需要手动调整这两个指针的位置,以达到回滚的操作。
第一步,对序列分块,询问排序。
第二步,指定ql为当前询问的l所在块的下一块的第一个元素,指定qr为当前询问l所在块的最后一个元素。对于每一个询问,我们要判断
- 如果当前询问的r也在l的块内,那么直接暴力统计区间答案,复杂度不超过 n−−√ n 。
- 如果当前询问的r不在l的块内,那么就让qr向右拓展,并暂存拓展结果,让ql向左拓展,然后统计区间
答案。这一步和普通的莫队没有区别。
第三步,回滚。我们让ql回滚到当前l所在块的下一块第一个元素,为下一次询问做准备。恢复暂存的拓展结果。
对于以上步骤,如果询问l所在块与上一个询问l所在块不同,就需要重新指定ql和pr。
由于在一个块内的询问,qr是不回退的,这就保证了复杂度!
如果还是不明白,可以看看下面的博客。
https://blog.csdn.net/MaxMercer/article/details/75576805
https://blog.csdn.net/qq_33330876/article/details/73522230
代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e5+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int n,m;
vector<ll> v;
ll t;
ll a[nmax],inocc[nmax],glocc[nmax],ans;
int belong[nmax],idx[nmax],sz,R[nmax];
struct node{
int l,r,id;
ll ans;
}Q[nmax];
int getid(ll x){return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;}
bool cmp(node a, node b){return (belong[a.l] == belong[b.l])?(a.r<b.r):(belong[a.l]<belong[b.l]);}
bool cmpid(node a, node b){return a.id < b.id;}
inline void modify(int pos,ll val){
glocc[idx[pos]] ++;
ans = max(ans,glocc[idx[pos]] * val);
}
inline void del(int pos){
glocc[idx[pos]] --;
}
int main(){
scanf("%d %d",&n,&m);
int sz = sqrt(n);
int num = n / sz; if(n%sz) num ++;
for(int i = 1;i<=num;++i) R[i] = min(i*sz,n);
for(int i = 1;i<=n;++i) scanf("%lld",&a[i]),v.push_back(a[i]);
sort(v.begin(),v.end()), v.erase(unique(v.begin(),v.end()),v.end()); // 离散化
for(int i = 1;i<=n;++i) idx[i] = getid(a[i]), belong[i] = (i-1) / sz + 1;
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 now = belong[Q[1].l];
int ql = R[now] + 1,qr = R[now];
for(int i = 1;i<=m;++i){
if(belong[Q[i].l] != now){
for(int j = 1;j<=n;++j) glocc[j] = 0;
now = belong[Q[i].l];
ql = R[now] + 1, qr = R[now];
ans = 0;
}
if(belong[Q[i].l] == belong[Q[i].r]){ // l和r在同一块
ll temp = 0;
for(int j = Q[i].l;j<=Q[i].r;++j) inocc[idx[j]] = 0;
for(int j = Q[i].l;j<=Q[i].r;++j) {
inocc[idx[j]] ++; temp = max(temp,inocc[idx[j]] * a[j]);
}
Q[i].ans = temp;
}else{ // l和r不在同一块
while(qr<Q[i].r) modify(qr+1,a[qr+1]),qr++;
ll temp = ans; // 暂存结果
while(ql>Q[i].l) modify(ql-1,a[ql-1]),ql--;
Q[i].ans = ans;
while(ql<R[now]+1) del(ql),ql++; //回退
ans = temp;
}
}
sort(Q+1,Q+1+m,cmpid);
for(int i = 1;i<=m;++i) printf("%lld\n",Q[i].ans);
return 0;
}