[分块] [Luogu AT_joisc2014_c] 历史研究 题解
题目描述
IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续
事件有种类之分。第
表示,
JOI 教授决定用如下的方法分析这些日记:
-
选择日记中连续的几天
作为分析的时间段; -
定义事件
的重要度 为 ,其中 为该事件在区间 中出现的次数。
现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度。
注意:教授有多组询问。
输入格式
第一行两个空格分隔的整数
接下来一行
接下来
输出格式
输出共有
数据范围
对于
样例输入 #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
题解
本体的实质是找一段区间内一个数出现的次数,可以考虑用分块来做;
定义
定义
这样,当我们查询时,如果
若不在同一个块,那么我们可以用
做此题的关键在于如何用
题目中的数据范围有时候也会给正解提供思路;
代码
#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
using namespace std;
long long a[1000005];
int cnt;
int b[1000005];
long long c[1000005];
int n, q;
int st[1000005], ed[1000005];
int belog[1000005];
int sq;
int sum[325][100005];
long long f[325][325];
long long t[1000005];
map<long long, int> mp;
long long ask(int l, int r) {
long long ans = 0;
if (belog[l] == belog[r]) { //直接暴力;
for (int i = l; i <= r; i++) {
t[b[i]]++;
}
for (int i = l; i <= r; i++) {
long long o = t[b[i]] * c[b[i]];
ans = max(ans, o);
}
for (int i = l; i <= r; i++) {
t[b[i]] = 0; //使用桶数组注意最后清零;
}
} else {
ans = f[belog[l] + 1][belog[r] - 1]; //中间整块的最大值;
for (int i = l; i <= ed[belog[l]]; i++) {
t[b[i]] = sum[belog[r] - 1][b[i]] - sum[belog[l]][b[i]]; //统计中间整块中b[i]出现的个数;
}
for (int i = st[belog[r]]; i <= r; i++) {
t[b[i]] = sum[belog[r] - 1][b[i]] - sum[belog[l]][b[i]];
}
for (int i = l; i <= ed[belog[l]]; i++) {
t[b[i]]++; //统计零散块中b[i]出现的个数;
}
for (int i = st[belog[r]]; i <= r; i++) {
t[b[i]]++;
}
for (int i = l; i <= ed[belog[l]]; i++) {
long long o = t[b[i]] * c[b[i]];
ans = max(ans, o); //更新答案;
}
for (int i = st[belog[r]]; i <= r; i++) {
long long o = t[b[i]] * c[b[i]];
ans = max(ans, o);
}
for (int i = l; i <= ed[belog[l]]; i++) {
t[b[i]] = 0; //使用桶数组注意最后清零;
}
for (int i = st[belog[r]]; i <= r; i++) {
t[b[i]] = 0; //使用桶数组注意最后清零;
}
}
return ans;
}
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) { //需要离散化;
cin >> a[i];
if (mp[a[i]] == 0) {
mp[a[i]] = ++cnt; //mp[a[i]]是a[i]离散化后对应的值;
}
b[i] = mp[a[i]]; //b数组是a数组离散化后对应的数组;
c[b[i]] = a[i]; //c[i] == j代表i这个离散化后的值对应的真实值为j;
}
sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
st[i] = sq * (i - 1) + 1;
ed[i] = sq * i;
}
ed[sq] = n;
for (int i = 1; i <= sq; i++) {
for (int j = st[i]; j <= ed[i]; j++) {
belog[j] = i;
}
}
for (int i = 1; i <= sq; i++) {
for (int j = 1; j <= ed[i]; j++) {
sum[i][b[j]]++;
}
}
for (int i = 1; i <= sq; i++) {
for (int j = 1; j <= sq; j++) {
long long ma = f[i][j - 1];
for (int k = st[j]; k <= ed[j]; k++) {
t[b[k]] = sum[j][b[k]] - sum[i - 1][b[k]];
}
for (int k = st[j]; k <= ed[j]; k++) {
if (ma < t[b[k]] * c[b[k]]) ma = t[b[k]] * c[b[k]];
}
f[i][j] = ma;
for (int k = st[j]; k <= ed[j]; k++) {
t[b[k]] = 0; //使用桶数组注意最后清零;
}
}
}
int l, r;
for (int i = 1; i <= q; i++) {
cin >> l >> r;
cout << ask(l, r) << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!