BZOJ4241: 历史研究
题目地址
题解
经典大分块..不过挺好写的。就写了半个小时左右。
处理出
- \(ans[i][j]\)表示块\(i\)到块\(j\)的答案
- \(cnt[i][j]\)表示块\(1\)到块\(i\)中数\(j\)的出现次数(要先离散化)
预处理这两个数组都离散化后利用一个桶就可以了。
考虑每个询问\([l,r]\)的答案可能的来源,首先肯定\(ans[l][r]\)是其中的一个候选项,然后两个边界块中的数也都是可能的候选项。那么枚举这两个块中的数,利用\(cnt\)数组和一个桶即可解决。
复杂度\(O(n \sqrt{n})\)
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100010;
const int M = 320;
inline void read(int &x) {
x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
}
ll ans[M][M];
int bl[N], L[M], R[M], cnt[M][N];
int a[N], b[N], c[N], tong[N];
int n, m, block, siz;
void init() {
siz = sqrt(n);
block = n / siz;
if(n % siz) ++block;
for(int i = 1; i <= n; ++i) {
bl[i] = (i - 1) / siz + 1;
}
for(int i = 1; i <= block; ++i) {
L[i] = (i - 1) * siz + 1;
R[i] = i * siz;
}
R[block] = n;
for(int i = 1; i <= block; ++i) {
for(int j = L[i]; j <= R[i]; ++j) cnt[i][a[j]]++;
}
for(int j = 1; j <= n; ++j) for(int i = 1; i <= block; ++i)
cnt[i][j] += cnt[i - 1][j];
for(int i = 1; i <= block; ++i) {
for(int k = L[i]; k <= R[i]; ++k) {
tong[a[k]]++;
ans[i][i] = max(ans[i][i], 1ll * tong[a[k]] * c[k]);
}
for(int j = i + 1; j <= block; ++j) {
ans[i][j] = ans[i][j - 1];
for(int k = L[j]; k <= R[j]; ++k) {
tong[a[k]]++;
ans[i][j] = max(ans[i][j], 1ll * tong[a[k]] * c[k]);
}
}
for(int j = L[i]; j <= n; ++j) tong[a[j]] = 0;
}
}
ll solve(int l, int r) {
ll Ans = 0;
if(bl[l] == bl[r] || bl[r] == bl[l] + 1) {
for(int i = l; i <= r; ++i) {
tong[a[i]]++;
Ans = max(Ans, 1ll * tong[a[i]] * c[i]);
}
for(int i = l; i <= r; ++i) tong[a[i]] = 0;
return Ans;
}
Ans = ans[bl[l] + 1][bl[r] - 1];
for(int i = l; i <= R[bl[l]]; ++i) {
tong[a[i]]++;
Ans = max(Ans, 1ll * (tong[a[i]] + cnt[bl[r] - 1][a[i]] - cnt[bl[l]][a[i]]) * c[i]);
}
for(int i = L[bl[r]]; i <= r; ++i) {
tong[a[i]]++;
Ans = max(Ans, 1ll * (tong[a[i]] + cnt[bl[r] - 1][a[i]] - cnt[bl[l]][a[i]]) * c[i]);
}
for(int i = l; i <= R[bl[l]]; ++i) tong[a[i]] = 0;
for(int i = L[bl[r]]; i <= r; ++i) tong[a[i]] = 0;
return Ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin);
#endif
read(n); read(m);
for(int i = 1; i <= n; ++i) {
read(a[i]);
b[i] = c[i] = a[i];
}
sort(b + 1, b + n + 1);
for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
init();
while(m--) {
int l, r; read(l); read(r);
printf("%lld\n", solve(l, r));
}
}