看到“子序列的子序列”这样的问题,首先想到莫队,即离线排序之后不断移动指针。下面以 [l,r−1] [ l , r − 1 ] 转移到 [l,r] [ l , r ] 为例,分析转移的方法。
可以看出, [l,r] [ l , r ] 的结果比 [l,r−1] [ l , r − 1 ] 的结果多了 ∑ri=lminrj=ia[j] ∑ i = l r min j = i r a [ j ] ,也就是 [l,r] [ l , r ] , [l+1,r] [ l + 1 , r ] , [l+2,r] [ l + 2 , r ] , ... . . . , [r−1,r] [ r − 1 , r ] , [r,r] [ r , r ] 这 r−l+1 r − l + 1 个子序列的最小值之和。
可以看出,如果设 x=maxr−1i=1i[a[i]<a[r]] x = max i = 1 r − 1 i [ a [ i ] < a [ r ] ] (也就是 x x 等于满足 i<r i < r 且 a[i]<a[r] a [ i ] < a [ r ] 的最大的 i i ),那么左端点在 [x+1,r] [ x + 1 , r ] 区间内,右端点为 r r 的所有 r−x r − x 个子序列的最小值都是 a[r] a [ r ] 。
同样也可以得到:如果设 y y 等于满足 i<x i < x 且 a[i]<a[x] a [ i ] < a [ x ] 的最大的 i i ,那么左端点在 [y+1,x] [ y + 1 , x ] 区间内,右端点为 r r 的所有 x−y x − y 个子序列的最小值都是 a[x] a [ x ] 。
这样,预处理出 pre[i] p r e [ i ] 等于满足 j<i j < i 且 a[j]<a[i] a [ j ] < a [ i ] 的最大的 j j (可以用单调栈得出),那么可以预处理一个类似于前缀和的东西,即
#include
#include
#include
#include
#include
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, LogN = 20;
int n, m, S, a[N], pre[N], suf[N], RMQ[N][LogN], Log[N], stk[N], top;
ll now, suml[N], sumr[N], res[N];
void init_rmq() {
int i, j; Log[0] = -1;
for (i = 1; i <= n; i++) Log[i] = Log[i >> 1] + 1, RMQ[i][0] = i;
for (j = 1; j <= 18; j++)
for (i = 1; i + (1 << j) - 1 <= n; i++) {
int l = RMQ[i][j - 1], r = RMQ[i + (1 << j - 1)][j - 1];
RMQ[i][j] = a[l] < a[r] ? l : r;
}
stk[top = 0] = 0; for (i = 1; i <= n; i++) {
while (top && a[stk[top]] >= a[i]) top--;
pre[stk[++top] = i] = stk[top - 1];
}
stk[top = 0] = n + 1; for (i = n; i; i--) {
while (top && a[stk[top]] > a[i]) top--;
suf[stk[++top] = i] = stk[top - 1];
}
for (i = 1; i <= n; i++) suml[i] = suml[pre[i]] + 1ll * a[i] * (i - pre[i]);
for (i = n; i; i--) sumr[i] = sumr[suf[i]] + 1ll * a[i] * (suf[i] - i);
}
int qmin(int l, int r) {
int x = Log[r - l + 1], y = RMQ[l][x], z = RMQ[r - (1 << x) + 1][x];
return a[y] < a[z] ? y : z;
}
struct cyx {
int l, r, bl, id; ll ans;
} que[N];
inline bool comp(const cyx &a, const cyx &b) {
if (a.bl != b.bl) return a.bl < b.bl;
return a.r < b.r;
}
ll queryle(int l, int r) {
int x = qmin(l, r); if (x == l) return 1ll * a[x] * (r - l + 1);
return 1ll * a[x] * (r - x + 1) + sumr[l] - sumr[x];
}
ll queryri(int l, int r) {
int x = qmin(l, r); if (x == r) return 1ll * a[x] * (r - l + 1);
return 1ll * a[x] * (x - l + 1) + suml[r] - suml[x];
}
int main() {
int i; n = read(); m = read(); S = sqrt(n);
for (i = 1; i <= n; i++) a[i] = read(); init_rmq();
for (i = 1; i <= m; i++) que[i].l = read(), que[i].r = read(),
que[i].id = i, que[i].bl = (que[i].l - 1) / S + 1;
sort(que + 1, que + m + 1, comp); int l = 1, r = 0;
for (i = 1; i <= m; i++) {
int tl = que[i].l, tr = que[i].r;
while (r < tr) now += queryri(l, ++r);
while (l > tl) now += queryle(--l, r);
while (r > tr) now -= queryri(l, r--);
while (l < tl) now -= queryle(l++, r);
que[i].ans = now;
}
for (i = 1; i <= m; i++) res[que[i].id] = que[i].ans;
for (i = 1; i <= m; i++) printf("%lld\n", res[i]);
return 0;
}
同样,利用单调栈,预处理出:
prei p r e i :在 i i 的左边,值比 ai a i 小并且与 i i 最近的位置。
sufi s u f i :在 i i 的右边,值比 ai a i 小或与 ai a i 相等,并且与 i i 最接近的位置。
求整个序列的最小值之和,可以反过来考虑每个 ai a i 作为最小值出现了多少次。
整个序列的最小值之和为:
#include
#include
#include
#include
#include
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, M = N << 1, L = M << 1;
int n, m, tot, a[N], pre[N], suf[N], top, stk[N]; ll ans[N];
struct cyx {
int t, l, r; ll dx, dy; cyx() {}
cyx(int _t, int _l, int _r, ll _dx, ll _dy) :
t(_t), l(_l), r(_r), dx(_dx), dy(_dy) {}
} orz[M];
struct pyz {int l, r, id;} que[N]; ll sumx[L], sumy[L], addx[L], addy[L];
inline bool comp1(cyx &a, cyx &b) {return a.t > b.t;}
inline bool comp2(pyz &a, pyz &b) {return a.l > b.l;}
void down(int p) {
addx[p2] += addx[p]; addx[p3] += addx[p]; addx[p] = 0;
addy[p2] += addy[p]; addy[p3] += addy[p]; addy[p] = 0;
}
void upt(int l, int r, int p) {
int mid = l + r >> 1; sumx[p] = sumx[p2] + sumx[p3] + addx[p2] *
(mid - l + 1) + addx[p3] * (r - mid); sumy[p] = sumy[p2] + sumy[p3]
+ addy[p2] * (mid - l + 1) + addy[p3] * (r - mid);
}
void change(int l, int r, int s, int e, ll dx, ll dy, int p) {
if (l == s && r == e) return (void) (addx[p] += dx, addy[p] += dy);
int mid = l + r >> 1; down(p);
if (e <= mid) change(l, mid, s, e, dx, dy, p2);
else if (s >= mid + 1) change(mid + 1, r, s, e, dx, dy, p3);
else change(l, mid, s, mid, dx, dy, p2),
change(mid + 1, r, mid + 1, e, dx, dy, p3); upt(l, r, p);
}
ll ask(int l, int r, int s, int e, int x, int p) {
if (l == s && r == e) return (sumx[p] + addx[p] * (r - l + 1))
* x + sumy[p] + addy[p] * (r - l + 1);
int mid = l + r >> 1; down(p); ll res;
if (e <= mid) res = ask(l, mid, s, e, x, p2);
else if (s >= mid + 1) res = ask(mid + 1, r, s, e, x, p3);
else res = ask(l, mid, s, mid, x, p2) + ask(mid + 1, r, mid + 1, e, x, p3);
return upt(l, r, p), res;
}
int main() {
int i, j; n = read(); m = read(); for (i = 1; i <= n; i++) a[i] = read();
stk[top = 0] = 0; for (i = 1; i <= n; i++) {
while (top && a[stk[top]] >= a[i]) top--;
pre[stk[++top] = i] = stk[top - 1];
}
stk[top = 0] = n + 1; for (i = n; i; i--) {
while (top && a[stk[top]] > a[i]) top--;
suf[stk[++top] = i] = stk[top - 1];
}
for (i = 1; i <= n; i++) {
orz[++tot] = cyx(i, i, suf[i] - 1, -a[i], 1ll * (i + 1) * a[i]);
orz[++tot] = cyx(pre[i], i, suf[i] - 1, a[i], -1ll * (pre[i] + 1) * a[i]);
}
for (i = 1; i <= m; i++) que[i].l = read(), que[i].r = read(), que[i].id = i;
sort(orz + 1, orz + tot + 1, comp1); sort(que + 1, que + m + 1, comp2);
for (i = j = 1; i <= m; i++) {
while (j <= tot && orz[j].t >= que[i].l) change(1, n, orz[j].l,
orz[j].r, orz[j].dx, orz[j].dy, 1), j++;
ans[que[i].id] = ask(1, n, que[i].l, que[i].r, que[i].l, 1);
}
for (i = 1; i <= m; i++) printf("%lld\n", ans[i]);
return 0;
}