BZOJ 4540 [Hnoi2016]序列 (单调栈 + ST表 + 莫队算法)
题目链接 BZOJ4540
考虑莫队算法。
这题难在$[l, r]$到$[l, r+1]$的转移。
根据莫队算法的原理,这个时候答案应该加上
$cal(l, r+1) + cal(l+1, r+1) + cal(l+2, r+1) + ... + cal(r+1, r+1)$
$cal(l, r)$表示$a[l], a[l+1], a[l+2], ..., a[r]$中的最小值。
我们先求出$[l, r +1]$ 这些数中的最小值$a[x]$
那么$cal(l, r+1) + cal(l+1, r+1) + cal(l+2, r+1) + ... + cal(x, r+1)$这一部分就解决了
这一部分的值为$(x - l + 1) * a[x]$
剩下还有一部分$cal(x+1, r+1) + cal(x+2, r+1) + ... + cal(r+1, r+1)$
考虑用单调栈求出两个数组$lc[], rc[]$。
$lc[i]$表示$a[i]$左边第一个小于$a[i]$的数的位置(如果没有则为$0$)
$rc[i]$表示$a[i]$右边第一个小于$a[i]$的数的位置(如果没有则为$n+1$)
然后我们维护两个序列$s1[], s2[]$
$s1[i] = s1[lc[i]] + (i - lc[i]) * a[i]$
刚刚那个剩余的情况中,$s1[r+1] - s1[x]$即为这部分的答案。
因为满足$a[r+1]>=a[x]$,所以对于$a[r+1]$来说,他往左不断找第一个小于他的数,
肯定能在某一步找到$a[x]$.
而我们维护$s1[]$的目的就是把这个跳的过程做到$O(1)$.
转移就是这样
那么另外三种情况也差不多,以此类推。
时间复杂度$O(n^{\frac{3}{2}}logn)$
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 1e5 + 10; const int A = 20; int n, m; int lc[N], rc[N], lg[N], belong[N]; int f[N][A]; int bs, l, r, x; stack <int> s; LL ans; LL a[N]; LL s1[N], s2[N]; LL ret[N]; struct node{ int l, r, id; friend bool operator < (const node &a, const node &b){ return belong[a.l] == belong[b.l] ? a.r < b.r : belong[a.l] < belong[b.l]; } } q[N]; inline int Min(int x, int y){ return a[x] < a[y] ? x : y;} void ST(){ rep(i, 1, n) f[i][0] = i; rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) f[i][j] = Min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); } inline int solve(int l, int r){ int k = lg[r - l + 1]; return Min(f[l][k], f[r - (1 << k) + 1][k]); } int main(){ rep(i, 2, 1e5) lg[i] = lg[i >> 1] + 1; scanf("%d%d", &n, &m); rep(i, 1, n) scanf("%lld", a + i); ST(); rep(i, 1, n){ while (!s.empty() && a[s.top()] >= a[i]) s.pop(); if (s.empty()) lc[i] = 0; else lc[i] = s.top(); s.push(i); } while (!s.empty()) s.pop(); dec(i, n, 1){ while (!s.empty() && a[s.top()] >= a[i]) s.pop(); if (s.empty()) rc[i] = n + 1; else rc[i] = s.top(); s.push(i); } rep(i, 1, n) s1[i] = s1[lc[i]] + (i - lc[i]) * a[i]; dec(i, n, 1) s2[i] = s2[rc[i]] + (rc[i] - i) * a[i]; bs = sqrt(n); rep(i, 1, n) belong[i] = (i - 1) / bs + 1; rep(i, 1, m){ scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i; } sort(q + 1, q + m + 1); l = 1, r = 0, ans = 0; rep(i, 1, m){ while (r < q[i].r){ ++r; x = solve(l, r); ans += ((x - l + 1) * a[x] + s1[r] - s1[x]); } while (r > q[i].r){ x = solve(l, r); ans -= ((x - l + 1) * a[x] + s1[r] - s1[x]); --r; } while (l > q[i].l){ --l; x = solve(l, r); ans += ((r - x + 1) * a[x] + s2[l] - s2[x]); } while (l < q[i].l){ x = solve(l, r); ans -= ((r - x + 1) * a[x] + s2[l] - s2[x]); ++l; } ret[q[i].id] = ans; } rep(i, 1, m) printf("%lld\n", ret[i]); return 0; }