题目描述
IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续 N N N 天发生的事件,大约每天发生一件。
事件有种类之分。第 i i i 天发生的事件的种类用一个整数 X i X_i Xi 表示, X i X_i Xi 越大,事件的规模就越大。
JOI 教授决定用如下的方法分析这些日记:
- 选择日记中连续的几天 [ L , R ] [L, R] [L,R] 作为分析的时间段;
- 定义事件 A A A 的重要度 W A W_A WA 为 A × T A A \times T_A A×TA,其中 T A T_A TA 为该事件在区间 [ L , R ] [L, R] [L,R] 中出现的次数。
现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度。
注意:教授有多组询问。
输入格式
第一行两个空格分隔的整数 N N N 和 Q Q Q,表示日记一共记录了 N N N 天,询问有 Q Q Q 次。
接下来一行 N N N 个空格分隔的整数表示每天的事件种类。
接下来 Q Q Q 行,每行给出 L , R L, R L,R 表示一组询问。
输出格式
输出共有 Q Q Q 行,每行一个整数,表示对应的询问的答案。
样例
样例输入1:
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
样例输出1:
9
8
8
16
16
样例输入2:
8 4
9 9 19 9 9 15 9 19
1 4
4 6
3 5
5 8
样例输出2:
27
18
19
19
样例输入3:
12 15
15 9 3 15 9 3 3 8 16 9 3 17
2 7
2 5
2 2
1 12
4 12
3 6
11 12
1 7
2 6
3 5
3 10
7 10
1 4
4 8
4 8
样例输出3:
18
18
9
30
18
15
17
30
18
15
18
16
30
15
15
数据范围
对于 100 % 100\% 100% 的数据, 1 ≤ N , Q ≤ 1 0 5 1 \le N, Q \le 10^5 1≤N,Q≤105, 1 ≤ X ≤ 1 0 9 1 \le X \le 10^9 1≤X≤109, 1 ≤ L , R ≤ 1 0 5 1 \le L, R \le 10^5 1≤L,R≤105。
题解
一道回滚莫队模板题。
由于求最大值是一种只增信息(即可以增加一个数求最大值但不能删除),所以显然不能用普通的莫队。
因此,考虑使用回滚莫队,将询问的左端点排序分块,块内按右端点从小到大排序。不能使用奇偶优化,因为这样会破坏右指针的单调性。
当左端点换块时:清空所有数据,直接将左右指针移到下一个块的右端点处。
对于块内询问:
- 若左右端点同块:直接从 l l l 到 r r r 枚举,可以保证复杂度。
- 若左右端点异块:首先保证 r r r 单调递增, l l l 指针进行回滚。每次询问时,先将右指针向右移动,记录下此时的 a n s ans ans 为 t t t,左指针再移动到应该的位置,得到询问的答案,然后将 a n s ans ans 还原为 t t t。
#include<bits/stdc++.h>
using namespace std;
int n, m, k;
int block;
int a[100010], d[100010], bl[100010], t[100010];
struct node{
int l, r, time;
}b[100010];
bool operator<(node p, node q){
return (bl[p.l] != bl[q.l]) ? (p.l < q.l) : (p.r < q.r);
}
int s[100010];
long long pp[100010];
long long ans = 0;
void add(int x){
s[x] ++;
ans = max(ans, (long long)s[x] * d[x]);
}
long long js(int l, int r){
long long ans = 0;
memset(t, 0, sizeof(t));
for(long long i = l; i <= r; ++ i){
t[a[i]] ++;
ans = max(ans, (long long)t[a[i]] * d[a[i]]);
}
return ans;
}
int main(){
read(n), read(m);
for(int i = 1; i <= n; ++ i){
read(a[i]);
d[i] = a[i];
}
for(int i = 1; i <= m; ++ i){
read(b[i].l), read(b[i].r);
b[i].time = i;
}
block = pow(n, 0.5);
for(int i = 1; i <= n; ++ i){
bl[i] = (i - 1) / block + 1;
}
sort(d + 1, d + n + 1);
for(int i = 1; i <= n; ++ i){
a[i] = lower_bound(d + 1, d + n + 1, a[i]) - d;
}
int pl = 1, pr = 0, pt = 0;
sort(b + 1, b + m + 1);
for(int i = 1; i <= bl[n]; ++ i){
memset(s, 0, sizeof(s));
int div = min(i * block, n);
pl = div + 1, pr = div;
ans = 0;
while(bl[b[pt + 1].l] == i){
++ pt;
if(bl[b[pt].l] == bl[b[pt].r]){
pp[b[pt].time] = js(b[pt].l, b[pt].r);
continue;
}
while(pr < b[pt].r){
++ pr;
add(a[pr]);
}
long long res = ans;
while(pl > b[pt].l){
-- pl;
add(a[pl]);
}
pp[b[pt].time] = ans;
ans = res;
while(pl <= div){
s[a[pl]] --;
++ pl;
}
}
}
for(int i = 1; i <= m; ++ i){
printf("%lld\n", pp[i]);
}
return 0;
}