博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj4540
阅读量:5149 次
发布时间:2019-06-13

本文共 2729 字,大约阅读时间需要 9 分钟。

莫队+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
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;}
View Code

莫队一定要先动r

转载于:https://www.cnblogs.com/19992147orz/p/7927934.html

你可能感兴趣的文章
jQuery on(),live(),trigger()
查看>>
treegrid.bootstrap使用说明
查看>>
[Docker]Docker拉取,上传镜像到Harbor仓库
查看>>
导航,头部,CSS基础
查看>>
[USACO 2017 Feb Gold] Tutorial
查看>>
gzip
查看>>
转负二进制(个人模版)
查看>>
LintCode-Backpack
查看>>
查询数据库锁
查看>>
我对于脚本程序的理解——百度轻应用有感
查看>>
面试时被问到的问题
查看>>
注解小结
查看>>
list control控件的一些操作
查看>>
一月流水账
查看>>
判断字符串在字符串中
查看>>
Linux环境下Redis安装和常见问题的解决
查看>>
HashPump用法
查看>>
cuda基础
查看>>
Vue安装准备工作
查看>>
oracle 创建暂时表
查看>>