bzoj4540
莫队+st表
据说这是经典问题,但是我不会。。。
问题在于莫队怎么算贡献,每次移动一个位置,现在为[l,r],那么就增加了[l-1,r),r的贡献,怎么算呢?我们预处理fl,fr,fl[i]表示以i为开头的前缀和,fr表示以i为结尾的后缀和,这个东西能够相减,但也不是完全满足
每次我们计算贡献的时候,设最小值的位置为p,那么对于右端点来说,贡献就是fr[r]-fr[p]+(p - l + 1) * a[p],这也是因为不完全满足可减性,因为求前缀和是fr[i]=(i - l[i] + 1) * a[i] + fr[l[i]-1],那么满足可减性是在每个l[i]的时候可以减,又因为p作为最小值肯定是处于一个l的位置,那么就能减了,而到l的贡献就是p作为最小值。
理解得还不是很透彻 我一直以为我的单调栈是左闭右闭的,竟然是左开右闭的
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n, m, block, l = 1, r, top; ll sum; int st[N], Log[N], f[N][18], L[N], R[N]; ll fl[N], fr[N], a[N], ans[N]; struct query { int l, r, id; bool friend operator < (const query &a, const query &b) { return (a.l - 1) / block == (b.l - 1) / block ? a.r < b.r : (a.l - 1) / block < (b.l - 1) / block; } } q[N]; inline int rd() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return x * f; } int Min(int x, int y) { return a[x] < a[y] ? x : y; } int rmq(int l, int r) { int x = Log[r - l + 1]; return Min(f[l][x], f[r - (1 << x) + 1][x]); } void addl(int l, int r, ll f) { int p = rmq(l, r); sum += f * (fl[l] - fl[p] + a[p] * (ll)(r - p + 1)); } void addr(int l, int r, ll f) { int p = rmq(l, r); sum += f * (fr[r] - fr[p] + a[p] * (ll)(p - l + 1)); } int main() { n = rd(); m = rd(); block = sqrt(n); for(int i = 1; i <= n; ++i) { a[i] = rd(); f[i][0] = i; } for(int i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1; for(int j = 1; j <= 17; ++j) for(int i = 1; i + (1 << j) <= n + 1; ++i) f[i][j] = Min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); for(int i = 1; i <= m; ++i) { q[i].l = rd(); q[i].r = rd(); q[i].id = i; } sort(q + 1, q + m + 1); for(int i = 1; i <= n; ++i) { L[i] = R[i] = i; while(top && a[st[top]] > a[i]) { R[st[top - 1]] = R[st[top]]; L[i] = L[st[top]]; --top; } st[++top] = i; } while(top) { R[st[top - 1]] = R[st[top]]; --top; } for(int i = 1; i <= n; ++i) fr[i] = fr[L[i] - 1] + (ll)(i - L[i] + 1) * a[i]; for(int i = n; i; --i) fl[i] = fl[R[i] + 1] + (ll)(R[i] - i + 1) * a[i]; l = 1; r = 0; for(int i = 1; i <= m; ++i) { while(r < q[i].r) addr(l, ++r, 1); while(r > q[i].r) addr(l, r--, -1); while(l < q[i].l) addl(l++, r, -1); while(l > q[i].l) addl(--l, r, 1); ans[q[i].id] = sum; } for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]); return 0; }
莫队一定要先动r