loj#2874. 「JOISC 2014 Day1」历史研究
分析
本题可以作为回滚莫队的入门题。回滚莫队是用于解决一类具有莫队特征、且加删点操作有一个很简单而另一个很难的时候可以使用的算法,复杂度同样为根号级。
举例而言,本题题意等价于每次询问给定区间,求区间内每个数的值乘以其出现次数之积的最大值。显然,这个问题中加点操作很简单,而朴素的删点需要反复维护次大值,不可做。
对于这类型的题目,我们可以思考如何尽量执行加点操作而不执行删点操作。可以想到,询问分块后按照区间左端点所在块为第一关键字,右端点为第二关键字排序。
对于每个询问:
-
如果左右端点在同一个块内,我们可以暴力计算,复杂度显然是根号。
-
否则,分别考虑左右端点的处理。对于右端点,由于同块内有序,所以可以直接加点。对于左端点,由于可能无序,所以每次都从块的右端点开始向左端点加点。每个询问的左端点最多可以加根号次,而右端点总共加\(n\)次,所以最后的复杂度仍然是\(n\sqrt{n}\)的。
于是我们就解决了这类型的问题。需要注意的是,回滚莫队的排序方式影响正确性,而普通莫队的排序只影响时间。
代码
/*
By Nero Claudius Caeser Augustus Germanicus,
Imperatorum Romanorum.
*/
#include <bits/stdc++.h>
using namespace std;
namespace StandardIO{
template<typename T>void read(T &x){
x=0;T f=1;char c=getchar();
for(; c<'0'||c>'9'; c=getchar()) if(c=='-') f=-1;
for(; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>void write(T x){
if(x<0) putchar('-'),x*=-1;
if(x>=10) write(x/10);
putchar(x%10+'0');
}
} using namespace StandardIO;
namespace Project{
#define int long long
const int N=100000+100;
int n,q,block;
int a[N],b[N],belong[N];
struct node{
int l,r,id;
bool operator < (const node x)const{
return (belong[l]==belong[x.l])?r<x.r:(belong[l]<belong[x.l]);
}
} ask[N];
int cnt[N],ans[N],ver=1,cnt2[N],res;
int calc(int l,int r){
int res=0;
for(int i=l; i<=r; ++i) cnt2[a[i]]=0;
for(int i=l; i<=r; ++i){
++cnt2[a[i]];
res=max(res,cnt2[a[i]]*b[a[i]]);
}
return res;
}
void add(int x){
++cnt[a[x]];
res=max(res,cnt[a[x]]*b[a[x]]);
}
void solve(int x){
int rr=min(x*block,n),qr=rr;res=0;
memset(cnt,0,sizeof(cnt));
while(belong[ask[ver].l]==x){
if(belong[ask[ver].l]==belong[ask[ver].r]){
ans[ask[ver].id]=calc(ask[ver].l,ask[ver].r),++ver;continue;
}
while(qr<ask[ver].r) add(++qr);
int last=res;
for(int i=ask[ver].l; i<=rr; ++i) add(i);
ans[ask[ver].id]=res;
for(int i=ask[ver].l; i<=rr; ++i) --cnt[a[i]];
res=last;
++ver;
}
}
void MAIN(){
read(n),read(q),block=sqrt(n);
for(int i=1; i<=n; ++i){
read(a[i]),b[i]=a[i];
}
sort(b+1,b+n+1);int len=unique(b+1,b+n+1)-b-1;
for(int i=1; i<=n; ++i){
a[i]=lower_bound(b+1,b+len+1,a[i])-b;
belong[i]=(i-1)/block+1;
}
for(int i=1; i<=q; ++i){
read(ask[i].l),read(ask[i].r),ask[i].id=i;
}
sort(ask+1,ask+q+1);
for(int i=1; i<=belong[n]; ++i){
solve(i);
}
for(int i=1; i<=q; ++i){
write(ans[i]),puts("");
}
}
#undef int
}
int main(){
Project::MAIN();
}