Description
给定长度为n的序列:a1,a2,…,an,记为a[1:n]。类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-
1,ar。若1≤l≤s≤t≤r≤n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1≤l≤r
≤n,求a[l:r]的不同子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有
6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。
Sample Input
5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5
Sample Output
28
17
11
11
17
这题竟然是我莫队第一题。。。
其实很久之前就莫队思想,只是不想打,结果这道题想来想去只能想到莫队一种想法,就写了一下莫队。
我考虑维护一个sum1,一个sum2
sum1[i]表示1~i、2~i、3~i、…、i~i最小值的和。
sum2[i]就反过来搞。
我们考虑莫队右端点的移位,我们把区间的最小值求出来,位置设为hh,那么l~r、l+1~r…hh~r的最小值其实全部都是a[hh],那么hh往左的部分就OK了,那么hh往右的部分其实只用sum1[r]-sum[hh]就可求出。
怎么证明?
sum1[hh]范围是1~hh、2~hh、3~hh、…、hh~hh
sum1[r]范围是1~r、2~r、3~r、…、r~r
我们发现l~hh、l+1~hh…hh~hh和l~r、l+1~r…hh~r的最小值其实是一样的就是a[hh]。那么1~hh、2~hh…l-1~hh和1~r、2~r…l-1~r其实也是一样的,这个也很好想,没什么好说的。
往左移其实反过来就好了。
注意中间有一个求最小值的过程我们需要一个st表来维护。
那么我们怎么求sum1,sum2呢?
考虑a[i]所能造成的影响,若离他最近的比a[i]小的位置为qq,那么qq+1~i这一段最小值就是a[i],那么就可得到sum[i]=sum[qq]+(i-qq)*a[i]。
怎么求离这个点最近的比他小的数呢?这一个我们考虑用单调栈维护。
毕竟第一次莫队没打过模版就写了,WA了半天。。。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
struct node {
int l, belong, r, p;
} q[110000];
struct tt {
int p, x;
} sta[110000]; int top;
int a[110000], f[110000][20], Log[110000];
LL sum1[110000], sum2[110000], pu[110000], ans;
int nowl, nowr;
int _min2(int x, int y) {return a[x] < a[y] ? x : y;}
bool cmp1(node a, node b) {
if(a.belong == b.belong) return a.r < b.r;
return a.belong < b.belong;
}
int query(int l, int r) {
int len = Log[r - l + 1];
return _min2(f[l][len], f[r - (1 << len) + 1][len]);
}
void p1(int l, int r, LL kk) {
int hh = query(l, r);
ans += (LL)(hh - l + 1) * (LL)a[hh] * kk;
ans += (sum1[r] - sum1[hh]) * kk;
}
void p2(int l, int r, LL kk) {
int hh = query(l, r);
ans += (LL)(r - hh + 1) * (LL)a[hh] * kk;
ans += (sum2[l] - sum2[hh]) * kk;
}
int main() {
int n, m; scanf("%d%d", &n, &m); int kk = sqrt(n);
Log[1] = 0;
for(int i = 2; i <= n; i++) {
if((1 << Log[i - 1] + 1) < i) Log[i] = Log[i - 1] + 1;
else Log[i] = Log[i - 1];
}
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), f[i][0] = i;
for(int i = 1; i <= m; i++) {
scanf("%d%d", &q[i].l, &q[i].r); q[i].p = i;
q[i].belong = _min((q[i].l - 1) / kk + 1, kk + 1);
}
sort(q + 1, q + m + 1, cmp1);
for(int j = 1; j <= 18; j++) {
for(int i = 1; i + (1 << j - 1) <= n; i++) {
f[i][j] = _min2(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
sta[1].p = 1; sta[1].x = a[1]; top = 1; sum1[1] = a[1];
for(int i = 2; i <= n; i++) {
while(top && a[i] <= sta[top].x) top--;
sum1[i] = sum1[sta[top].p] + (LL)a[i] * (i - sta[top].p);
sta[++top].x = a[i]; sta[top].p = i;
}
sta[0].p = n + 1; sta[1].p = n; sta[1].x = a[n]; top = 1; sum2[n] = a[n];
for(int i = n - 1; i >= 1; i--) {
while(top && a[i] <= sta[top].x) top--;
sum2[i] = sum2[sta[top].p] + (LL)a[i] * (sta[top].p - i);
sta[++top].x = a[i]; sta[top].p = i;
}
ans = 0;
nowl = 1; nowr = 0;
for(int i = 1; i <= m; i++) {
for(int j = nowr + 1; j <= q[i].r; j++) p1(nowl, j, 1);
for(int j = q[i].r + 1; j <= nowr; j++) p1(nowl, j, -1);
for(int j = nowl; j < q[i].l; j++) p2(j, q[i].r, -1);
for(int j = q[i].l; j < nowl; j++) p2(j, q[i].r, 1);
nowl = q[i].l, nowr = q[i].r;
pu[q[i].p] = ans;
}
for(int i = 1; i <= m; i++) printf("%lld\n", pu[i]);
return 0;
}