bzoj4241: 历史研究
Description
IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:
- 选择日记中连续的一些天作为分析的时间段
- 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
- 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
Input
第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1…XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。
Output
输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度
Sample Input
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
Sample Output
9
8
8
16
16
HINT
1<=N<=10^5
1<=Q<=10^5
1<=Xi<=10^9 (1<=i<=N)
分析
最大值这种操作莫队肯定是hold不住的。
原因是只能支持
O
(
1
)
O(1)
O(1)插入不能支持
O
(
1
)
O(1)
O(1)删除。
于是有了回滚莫队这种神奇的操作。
考虑莫队的时候
r
r
r是单调的,所以
r
r
r的操作可以保证只有插入(没换一次块重置即可)。
这时候发现
l
l
l每次挪动是不超过
n
\sqrt n
n
假设当前所有询问的
l
l
l分属同一个块,那么每一次都暴力地从这个块的右端点挪到
l
l
l,这样只有插入操作,可以求出
M
x
Mx
Mx。
这个时候有一种操作叫做回滚。虽然说最大值不能
O
(
1
)
O(1)
O(1)删除,但是桶可以。只要把这一次操作的
M
x
Mx
Mx消除,暴力“回滚”到原来的右端点即可。
这样每次左端点暴力的复杂度是
O
(
n
)
O(\sqrt n)
O(n),总复杂度还是
O
(
n
n
)
O(n\sqrt n)
O(nn)
莫队真是博大精深啊。
代码
#include<bits/stdc++.h>
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
const int N = 1e5 + 10;
struct Q {int l, r, id;}q[N];
int a[N], b[N], l[N], r[N], id[N], nn, n, m, B; long long c[N], A[N];
bool cmp(Q a, Q b) {return id[a.l] == id[b.l] ? a.r < b.r : id[a.l] < id[b.l];}
int F(int x) {
int L = 1, R = nn, m;
for(;L != R; b[m = L + R >> 1] >= x ? R = m : L = m + 1) ;
return L;
}
int main() {
n = ri(); m = ri(); B = sqrt(n) + 1;
for(int i = 1;i <= n; ++i)
a[i] = b[i] = ri();
for(int i = 1;i <= m; ++i)
q[i].l = ri(), q[i].r = ri(), q[i].id = i;
std::sort(b + 1, b + n + 1); nn = 1;
for(int i = 2;i <= n; ++i)
if(b[i] != b[i - 1]) b[++nn] = b[i];
for(int i = 1;i <= n; ++i)
a[i] = F(a[i]);
for(int i = 1;i <= n; ++i)
id[i] = (i - 1) / B + 1, r[id[i]] = i;
std::sort(q + 1, q + m + 1, cmp); long long mx;
for(int x = 1, ls = 0, L, R;x <= m; ++x) {
int st = q[x].l, ed = q[x].r; long long *Ans = &A[q[x].id];
if(id[st] != ls) {
std::memset(c, 0, sizeof(c));
ls = id[st]; R = r[id[st]] + 1;
mx = c[a[R]] = b[a[R]];
}
if(id[st] == id[ed]) {
c[a[R]] = 0;
for(int i = st; i <= ed; ++i)
c[a[i]] += b[a[i]], *Ans = std::max(*Ans, c[a[i]]);
for(int i = st; i <= ed; ++i)
c[a[i]] -= b[a[i]];
c[a[R]] = b[a[R]];
}
else {
for(;R < ed;)
++R, c[a[R]] += b[a[R]], mx = std::max(mx, c[a[R]]);
*Ans = mx;
for(int i = r[id[st]]; i >= st; --i)
c[a[i]] += b[a[i]], *Ans = std::max(*Ans, c[a[i]]);
for(int i = r[id[st]]; i >= st; --i)
c[a[i]] -= b[a[i]];
}
}
for(int i = 1;i <= m; ++i) printf("%lld\n", A[i]);
return 0;
}