「HNOI2016」序列
「HNOI2016」序列
传送门
考虑莫队,瓶颈在于如何 \(O(1)\) 的更新扩展后区间的答案。
我们只讨论左端点向左扩展的情况,其他情况类同。
假设从 \([l + 1, r]\) 扩展到 \([l, r]\) ,那么也就多出了 \([l, l], [l, l + 1] \cdots [l, r]\) 这些区间,考虑计算这些区间的答案。
首先找到这段区间的最小值所在位置 \(p\) ,那么右端点位于 \([p, r]\) 的区间的答案都是 \(a[p]\) 。
另一部分用前缀和算:记 \(f[i]\) 为 \(i\) 为左端点时到后面位置的贡献这个可以用单调栈预处理一下然后从后往前转移:\(f[i] = f[R[i]] + (R[i] - i) \times a[i]\)。\(R[i]\) 表示右边第一个比 \(a[i]\) 小的数的位置。
具体细节可以看代码。
参考代码:
#include <algorithm>
#include <cstdio>
#include <cmath>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
typedef long long LL;
const int _ = 1e5 + 5;
int n, m, q, a[_], pos[_], lg[_];
int top, stk[_], st[20][_], L[_], R[_]; LL fL[_], fR[_], res[_];
struct node { int l, r, id; } t[_];
inline bool cmp(const node& x, const node& y)
{ return pos[x.l] == pos[y.l] ? x.r < y.r : x.l < y.l; }
inline int query(int l, int r) {
int x = lg[r - l + 1];
int A = st[x][l], B = st[x][r - (1 << x) + 1];
return a[A] <= a[B] ? A : B;
}
inline LL uptL(int l, int r) {
int p = query(l, r);
return fL[l] - fL[p] + 1ll * (r - p + 1) * a[p];
}
inline LL uptR(int l, int r) {
int p = query(l, r);
return fR[r] - fR[p] + 1ll * (p - l + 1) * a[p];
}
int main() {
read(n), read(q), m = sqrt(n);
for (rg int i = 1; i <= n; ++i) read(a[i]), pos[i] = (i - 1) / m + 1;
for (rg int i = 1; i <= q; ++i) read(t[i].l), read(t[i].r), t[i].id = i;
sort(t + 1, t + q + 1, cmp);
stk[top = 0] = 0;
for (rg int i = 1; i <= n; ++i) {
while (top && a[stk[top]] > a[i]) --top;
L[i] = stk[top], stk[++top] = i;
}
stk[top = 0] = n + 1;
for (rg int i = n; i >= 1; --i) {
while (top && a[stk[top]] > a[i]) --top;
R[i] = stk[top], stk[++top] = i;
}
for (rg int i = 1; i <= n; ++i) fR[i] = fR[L[i]] + 1ll * (i - L[i]) * a[i];
for (rg int i = n; i >= 1; --i) fL[i] = fL[R[i]] + 1ll * (R[i] - i) * a[i];
for (rg int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
for (rg int i = 1; i <= n; ++i) st[0][i] = i;
for (rg int i = 1; i <= lg[n]; ++i)
for (rg int j = 1; j + (1 << i) - 1 <= n; ++j) {
int A = st[i - 1][j], B = st[i - 1][j + (1 << (i - 1))];
st[i][j] = a[A] <= a[B] ? A : B;
}
LL ans = 0;
for (rg int l = 1, r = 0, i = 1; i <= q; ++i) {
while (r < t[i].r) ans += uptR(l, ++r);
while (l > t[i].l) ans += uptL(--l, r);
while (r > t[i].r) ans -= uptR(l, r--);
while (l < t[i].l) ans -= uptL(l++, r);
res[t[i].id] = ans;
}
for (rg int i = 1; i <= q; ++i) printf("%lld\n", res[i]);
return 0;
}