抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

传送门

首先,如果这道题用的是普通的莫队,那么删除元素的时候,可能出现次数最大的那个元素的出现次数被减少至小于出现次数第二大的那个元素的出现次数,此时,你就没法知道出现次数第二大的元素的值,就咕咕了。

当然你也可以用一个set\rm set搞,但是估计会TLE\rm TLE

这里要介绍一种莫队,叫做回滚莫队。

先考虑莫队的排序方式,我们把整个序列分块,以左端点所在的块的编号为第一关键字,右端点所在的块的编号为第二关键字来排序。

不妨分块考虑,我们钦定左端点,让它就在其中一个块里面,当然右端点我们管不着。

如图,左端点ll在蓝色区域,右端点rr在红色区域。

根据排序的方式,发现右端点一直递增。

设蓝色区域左端点是LL,右端点是RR

分情况讨论:

1.rRr \le R

这时我们查询的就是类似于绿色区域的东东,暴力搞就可以了,时间复杂度为O(n)O(\sqrt n)

2.r>Rr>R

这时我们查询的就是类似于绿色区域的东东,不妨把绿色区域分成两块:

分成了[l,R],[R+1,r][l,R],[R+1,r]两块,发现[R+1,r][R+1,r]非常好搞,因为rr 是递增的,所以移动rr过程中统计即可。

但是[l,R][l,R]不好搞,ll没有单调性。

别忘了我们钦定了ll在同一块,所以暴力移动ll,时间复杂度O(n)O (\sqrt n),最后别忘了还原ll

所以,钦定ll 这种思路非常巧妙,不知道高到哪里去了。

形象一点来说,ll的移动非常繁忙,每来一个rr都要在块内移动一个来回,但是rr非常轻松,只要一直往右移动就行了。

代码如下,注意开long long\text{long long}

我感觉写起来不怎么优美。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <bits/stdc++.h>
#define MAXN 100005
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^'0');
ch=getchar();
}
return x*f;
}
int a[MAXN],b[MAXN],org[MAXN],pos[MAXN],n,m;
struct Query{
int l,r;
int id;
}q[MAXN];
inline bool operator < (const Query &A,const Query &B){
return pos[A.l]==pos[B.l]?A.r<B.r:pos[A.l]<pos[B.l];
}
inline void discrete(){
for (register int i=1;i<=n;++i) b[i]=a[i];
sort(b+1,b+1+n);
int s=unique(b+1,b+1+n)-b-1;
for (register int i=1;i<=n;++i){
a[i]=lower_bound(b+1,b+1+s,a[i])-b;
}
}
int Size,cnt[MAXN],Ans[MAXN];
inline int BruteForce(int l,int r){
//这里不能直接memset不然就不是O(sqrt(n))的了
for (register int i=l;i<=r;++i) cnt[a[i]]=0;
int ret=0;
for (register int i=l;i<=r;++i) {
ret=max(ret,org[i]*(++cnt[a[i]]));
}
return ret;
}
int c[MAXN],ans;
inline void Add(int x){
ans=max(ans,org[x]*(++c[a[x]]));
}
inline void Del(int x){
--c[a[x]];
}
inline int MoQueue(int i,int id){//返回下一个块的第一个询问的编号
int R=min(n,Size*id);
int l=R,r=R-1;
ans=0;
memset(c,0,sizeof(c));
while (pos[q[i].l]==id){
if (pos[q[i].l]==pos[q[i].r]) {
Ans[q[i].id]=BruteForce(q[i].l,q[i].r);
i++;
continue;
}
while (r<q[i].r) Add(++r);
int temp=ans;//记录的是[R+1,r]的答案
while (l>q[i].l) Add(--l);
Ans[q[i].id]=ans;
while (l<R) Del(l++);
ans=temp;
i++;
}
return i;
}
#undef int
int main(){
#define int long long
n=read(),m=read();
Size=(int)(sqrt(n));
for (register int i=1;i<=n;++i){
org[i]=a[i]=read();
pos[i]=(i-1)/Size+1;
}
discrete();
for (register int i=1;i<=m;++i){
int l=read(),r=read();
q[i]=Query{l,r,i};
}
sort(q+1,q+1+m);
int ptr=1;
for (register int i=1;i<=pos[n];++i){
ptr=MoQueue(ptr,i);
}
for (register int i=1;i<=m;++i){
printf("%lld\n",Ans[i]);
}
}

评论