luogu P3246 [HNOI2016]序列 莫隊+ST表
阿新 • • 發佈:2018-12-09
題意
- 次詢問,每次詢問一個區間所有子區間的最小值之和。
這個題想到莫隊還是不難,難的是怎樣做到 轉移,首先我們可以用 表預處理出區間最小值的位置,並且用單調棧找到每個數左右兩邊第一個比它小的數記為 。當我們從區間 轉移到 時,增加的區間就是 ,我們找到區間最小值的位置 後,對於區間 最小值顯然是 ,然後我們再計算 的貢獻,對於 這個位置產生的貢獻就是 ,這個東西可以寫成一個類似字首和的形式,並且最後一定會推到 這個位置上來,那麼這樣子就可以做到 轉移了,區間長度變短的時候就減去加的貢獻就好了,複雜度
Codes
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = log2(1e5) + 1;
long long ans[N], dpl[N], dpr[N], res;
int n, q, top, size, ST[N][M], lg[N];
int a[N], Sta[N], be[N], L[N], R[N];
struct Que {
int l, r, id;
bool operator < (const Que &T) const {
if (be[l] == be[T.l]) return r < T.r;
return be[l] < be[T.l];
}
}Q[N];
int _min(int x, int y) {return a[x] < a[y] ? x : y;}
int qmin(int x, int y) {
int len = lg[y - x + 1];
return _min(ST[x][len], ST[y - (1 << len) + 1][len]);
}
void insl(int l, int r) {
int pos = qmin(l, r);
res += 1ll * a[pos] * (r - pos + 1);
res += dpr[l] - dpr[pos];
}
void insr(int l, int r) {
int pos = qmin(l, r);
res += 1ll * a[pos] * (pos - l + 1);
res += dpl[r] - dpl[pos];
}
void dell(int l, int r) {
int pos = qmin(l, r);
res -= 1ll * a[pos] * (r - pos + 1);
res -= dpr[l] - dpr[pos];
}
void delr(int l, int r) {
int pos = qmin(l, r);
res -= 1ll * a[pos] * (pos - l + 1);
res -= dpl[r] - dpl[pos];
}
int main() {
#ifdef ylsakioi
freopen("3246.in", "r", stdin);
freopen("3246.out", "w", stdout);
#endif
scanf("%d%d", &n, &q), size = sqrt(n);
for (int i = 1; i <= n; ++ i)
scanf("%d", &a[i]), ST[i][0] = i;
for (int i = 1; i <= q; ++ i) {
scanf("%d%d", &Q[i].l, &Q[i].r);
Q[i].id = i, be[Q[i].l] = Q[i].l / size;
}
sort(Q + 1, Q + q + 1);
for (int i = 2; i <= n; ++ i)
lg[i] = lg[i >> 1] + 1;
for (int j = 1; j < M; ++ j)
for (int i = 1; i + (1 << j) - 1 <= n; ++ i)
ST[i][j] = _min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= n; ++ i) {
while (top && a[Sta[top]] > a[i]) R[Sta[top --]] = i;
L[i] = Sta[top], Sta[++ top] = i;
}
while (top) R[Sta[top --]] = n + 1;
for (int i = 1; i <= n; ++ i)
dpl[i] = dpl[L[i]] + 1ll * (i - L[i]) * a[i];
for (int i = n; i >= 1; -- i)
dpr[i] = dpr[R[i]] + 1ll * (R[i] - i) * a[i];
int l = 1, r = 0;
for (int i = 1; i <= q; ++ i) {
while (r < Q[i].r) insr(l, ++ r);
while (r > Q[i].r) delr(l, r --);
while (l > Q[i].l) insl(-- l, r);
while (l < Q[i].l) dell(l ++ , r);
ans[Q[i].id] = res;
}
for (int i = 1; i <= q; ++ i)
printf("%lld\n", ans[i]);
return 0;
}