题目链接:点击查看

题目大意:给出一个长度为 nnn 的序列,再给出 mmm 次询问,每次询问需要回答一个区间 [l,r][l,r][l,r] 内所有子区间的最小值之和

题目分析:因为可以离线,所以考虑莫队,这个题的难点是如何处理 [L,R][L,R][L,R] 递推到 [L,R+1][L,R+1][L,R+1]

从 [L,R][L,R][L,R] 推到 [L,R+1][L,R+1][L,R+1],实质上多了 R−L+2R-L+2R−L+2 个子区间,分别是 [L,R+1],[L+1,R+1],...,[R+1,R+1][L,R+1],[L+1,R+1],...,[R+1,R+1][L,R+1],[L+1,R+1],...,[R+1,R+1],我们只需要将这些子区间的最小值之和加上就好了

所以不难想到需要维护一个辅助数组 f[r]f[r]f[r],代表前缀 [1:r][1:r][1:r] 的所有后缀 [1:r],[2:r],...,[r:r][1:r],[2:r],...,[r:r][1:r],[2:r],...,[r:r] 的最小值之和,转移的话可以利用单调栈维护出 l[r]l[r]l[r] 代表 rrr 左侧首个小于 a[r]a[r]a[r] 的位置,那么有 f[r]=f[l[r]]+(r−l[r])∗a[r]f[r]=f[l[r]]+(r-l[r])*a[r]f[r]=f[l[r]]+(r−l[r])∗a[r],具体意义就是 a[r]a[r]a[r] 充当了左端点位于 (l[r],r](l[r],r](l[r],r] 的这些后缀的最小值

现在我们需要借助 fff 数组快速求出以 R+1R+1R+1 为右端点,左端点位于区间 [L,R+1][L,R+1][L,R+1] ,右端点为 R+1R+1R+1 的子区间的最小值之和,下文称最小值之和贡献

设区间 [L,R+1][L,R+1][L,R+1] 的最小值的位置为 ppp,不难看出左端点位于 [L,p][L,p][L,p] 的子区间的贡献为 a[p]a[p]a[p],现在我们的问题变成如何计算左端点位于 (p,R+1](p,R+1](p,R+1] 的子区间的贡献

用我们刚才的 fff 公式展开看看?

设 x=R+1x=R+1x=R+1 :f[x]=f[l[x]]+(x−l[x])∗a[x]f[x]=f[l[x]]+(x-l[x])*a[x]f[x]=f[l[x]]+(x−l[x])∗a[x]

设 pre=l[x]pre=l[x]pre=l[x] : f[pre]=f[l[pre]]+(pre−l[pre])∗a[pre]f[pre]=f[l[pre]]+(pre-l[pre])*a[pre]f[pre]=f[l[pre]]+(pre−l[pre])∗a[pre]

设 ppre=l[pre]ppre=l[pre]ppre=l[pre] : f[ppre]=f[l[ppre]]+(ppre−l[ppre])∗a[ppre]f[ppre]=f[l[ppre]]+(ppre-l[ppre])*a[ppre]f[ppre]=f[l[ppre]]+(ppre−l[ppre])∗a[ppre]

不难发现我们会沿着一个 a[pre]a[pre]a[pre] 递减的序列一直迭代,即 a[ppre]<a[pre]<a[x]a[ppre]<a[pre]<a[x]a[ppre]<a[pre]<a[x],并且直到 ppp 停止,因为 a[p]a[p]a[p] 是区间 [L,R+1][L,R+1][L,R+1] 中的最小值呀,a[p]a[p]a[p] 前面不会再有比它小的数字了

如果将上面的公式自底向上套进去的话,我们会惊奇的发现,f[R+1]−f[p]f[R+1]-f[p]f[R+1]−f[p] 即为所求,也就是左端点位于 (p,R+1](p,R+1](p,R+1] ,右端点为 R+1R+1R+1 的这些子区间的贡献

到此为止我们可以利用 RMQRMQRMQ 预处理区间最小值的位置然后 O(1)O(1)O(1) 进行区间 [L,R][L,R][L,R] 到 [L,R+1][L,R+1][L,R+1] 的扩展了,对于剩下的 [L,R−1],[L+1,R],[L−1,R][L,R-1],[L+1,R],[L-1,R][L,R−1],[L+1,R],[L−1,R] ,分析起来同理,这里不做过多赘述

有一个小问题就是,这个题的莫队的修改顺序需要先加后减,不然会又WA又RE的

那么莫队的时间复杂度就是 O(nlogn+nn)=O(nn)O(nlogn+n\sqrt{n})=O(n\sqrt{n})O(nlogn+nn​)=O(nn​)


打一个分割线,下面讲讲另一种写法

注意到这个题很巧妙地预处理出了 fff 数组辅助莫队的转移,所以我们既然想到了 fff 数组,为什么不思考一下能否直接用 fff 数组计算区间的贡献呢?

给定区间 [L,R][L,R][L,R],还是设 ppp 为区间最小值的位置,我们将总贡献分成三份:区间 [L,R][L,R][L,R] 的贡献 = 区间 [L,p)[L,p)[L,p) 的贡献 + 区间 (p,R](p,R](p,R] 的贡献 + (p−L+1)∗(R−p+1)∗a[p](p-L+1)*(R-p+1)*a[p](p−L+1)∗(R−p+1)∗a[p]

上述公式第三项 (p−L+1)∗(R−p+1)∗a[p](p-L+1)*(R-p+1)*a[p](p−L+1)∗(R−p+1)∗a[p] ,意思就是左端点位于 [L,p][L,p][L,p],同时右端点位于 [p,R][p,R][p,R] 的子区间的贡献

这里我们以区间 (p,R](p,R](p,R] 的贡献为例,分析一下该如何计算

前面的莫队解法我们提到了,f[R]−f[p]f[R]-f[p]f[R]−f[p] 代表的是,右端点为 RRR,左端点位于 (p,R](p,R](p,R] 的子区间的贡献

那么扩展到 f[R−1]−f[p]f[R-1]-f[p]f[R−1]−f[p] 是不是也可以代表,右端点为 R−1R-1R−1,左端点位于 (p,R−1](p,R-1](p,R−1] 的子区间的贡献?

所以区间 (p,R](p,R](p,R] 的贡献就可以写成(f[R]−f[p])+(f[R−1]−f[p])+...+(f[p+1]−f[p])(f[R]-f[p])+(f[R-1]-f[p])+...+(f[p+1]-f[p])(f[R]−f[p])+(f[R−1]−f[p])+...+(f[p+1]−f[p])

设 g[i]=∑j=1jf[j]g[i]=\sum\limits_{j=1}^{j}f[j]g[i]=j=1∑j​f[j],也就是数组 fff 的一个前缀和,那么上述公式就等价于 g[R]−g[p]−f[p]∗(R−p)g[R]-g[p]-f[p]*(R-p)g[R]−g[p]−f[p]∗(R−p)

所以现在本题的时空复杂度就变成 RMQRMQRMQ 预处理的复杂度了,也就是 O(nlogn)O(nlogn)O(nlogn),且强制在线

代码:
离线

// Problem: P3246 [HNOI2016]序列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3246
// Memory Limit: 500 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f;
}
template<typename T>
inline void write(T x)
{if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e5+100;
int size,n,m,a[N],l[N],r[N],mmin[N][20],Log[N];
LL fl[N],fr[N],ans[N];
struct query
{int l,r,id;bool operator<(const query& a)const{if(l/size!=a.l/size)return l<a.l;else if((l/size)&1)return r<a.r;elsereturn r>a.r;}
}q[N];
int cmp(int x,int y) {return a[x]<a[y]?x:y;
}
int ask(int l,int r) {int k=Log[r-l+1];return cmp(mmin[l][k],mmin[r-(1<<k)+1][k]);
}
LL left(int l,int r) {int p=ask(l,r);return 1LL*(r-p+1)*a[p]+fl[l]-fl[p];
}
LL right(int l,int r) {int p=ask(l,r);return 1LL*(p-l+1)*a[p]+fr[r]-fr[p];
}
void solve()
{int l=1,r=0;LL sum=0;for(int i=1;i<=m;i++){int ql=q[i].l;int qr=q[i].r;while(l>ql) sum+=left(l-1,r),l--;while(r<qr) sum+=right(l,r+1),r++;while(l<ql) sum-=left(l,r),l++;while(r>qr) sum-=right(l,r),r--;ans[q[i].id]=sum;}
}
void init() {size=313;//单调栈stack<int>st;for(int i=1;i<=n;i++) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) l[i]=0;else l[i]=st.top();st.push(i);}while(st.size()) st.pop();for(int i=n;i>=1;i--) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) r[i]=n+1;else r[i]=st.top();st.push(i);}//RMQfor(int i=1;i<=n;i++)mmin[i][0]=i,Log[i]=log2(i);for(int i=1;i<20;i++)for(int j=1;j+(1<<i)-1<=n;j++)mmin[j][i]=cmp(mmin[j][i-1],mmin[j+(1<<(i-1))][i-1]);//dpfor(int i=1;i<=n;i++) fr[i]=fr[l[i]]+1LL*(i-l[i])*a[i];for(int i=n;i>=1;i--) fl[i]=fl[r[i]]+1LL*(r[i]-i)*a[i];
}
int main()
{#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);read(n),read(m);for(int i=1;i<=n;i++) {read(a[i]);}init();for(int i=1;i<=m;i++) {read(q[i].l),read(q[i].r),q[i].id=i;}sort(q+1,q+1+m);solve();for(int i=1;i<=m;i++) {printf("%lld\n",ans[i]);}return 0;
}

在线

// Problem: P3246 [HNOI2016]序列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3246
// Memory Limit: 500 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#define lowbit(x) x&-x
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)
{T f=1;x=0;char ch=getchar();while(0==isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();x*=f;
}
template<typename T>
inline void write(T x)
{if(x<0){x=~(x-1);putchar('-');}if(x>9)write(x/10);putchar(x%10+'0');
}
const int inf=0x3f3f3f3f;
const int N=1e5+100;
int n,m,a[N],l[N],r[N],mmin[N][20],Log[N];
LL fl[N],fr[N],gl[N],gr[N];
int cmp(int x,int y) {return a[x]<a[y]?x:y;
}
int ask(int l,int r) {int k=Log[r-l+1];return cmp(mmin[l][k],mmin[r-(1<<k)+1][k]);
}
void init() {//单调栈stack<int>st;for(int i=1;i<=n;i++) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) l[i]=0;else l[i]=st.top();st.push(i);}while(st.size()) st.pop();for(int i=n;i>=1;i--) {while(st.size()&&a[st.top()]>a[i]) st.pop();if(st.empty()) r[i]=n+1;else r[i]=st.top();st.push(i);}//RMQfor(int i=1;i<=n;i++)mmin[i][0]=i,Log[i]=log2(i);for(int i=1;i<20;i++)for(int j=1;j+(1<<i)-1<=n;j++)mmin[j][i]=cmp(mmin[j][i-1],mmin[j+(1<<(i-1))][i-1]);//dpfor(int i=1;i<=n;i++) fr[i]=fr[l[i]]+1LL*(i-l[i])*a[i],gr[i]=gr[i-1]+fr[i];for(int i=n;i>=1;i--) fl[i]=fl[r[i]]+1LL*(r[i]-i)*a[i],gl[i]=gl[i+1]+fl[i];
}
int main()
{#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);read(n),read(m);for(int i=1;i<=n;i++) {read(a[i]);}init();while(m--) {int l,r;read(l),read(r);int p=ask(l,r);printf("%lld\n",1LL*(p-l+1)*(r-p+1)*a[p]+gr[r]-gr[p]-fr[p]*(r-p)+gl[l]-gl[p]-fl[p]*(p-l));}return 0;
}

洛谷 - P3246 [HNOI2016]序列(莫队+单调栈)相关推荐

  1. P3246 [HNOI2016]序列 莫队 + ST表 + 单调栈

    传送门 文章目录 题意: 思路: Update 题意: 思路: 比较神奇的一个题,这里先介绍莫队的离线解法. 不难发现,用莫队来做最大的难点就是在进行区间移动的时候如何快速计算贡献. 比如[l,r]− ...

  2. HDU - 6989 Didn‘t I Say to Make My Abilities Average in the Next Life?! 莫队/单调栈 + 线段树/ST表在线

    传送门 文章目录 题意: 思路: 题意: 思路: 考虑将贡献分开来算,先计算最大值,再算个最小值,之后答案就是((max+min)/2)/(len∗(len+1)/2)((max+min)/2)/(l ...

  3. P3246 [HNOI2016]序列(莫队+单调栈+ST表)

    [HNOI2016]序列 Tea神题解 Kelin神题解 对于莫队算法最主要的是如何快速算出[l,r]→[l,r+1][l,r]\to[l,r+1][l,r]→[l,r+1]对答案的贡献的变化. 当询 ...

  4. 洛谷1903 带修莫队

    /*洛谷1903*/ #include<cstdio> #include<algorithm> #include<iostream> #include<cma ...

  5. 洛谷 P2056 采花 - 莫队算法

    萧芸斓是 Z国的公主,平时的一大爱好是采花. 今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 1-c 表示) ,且花是排成一排的,以 ...

  6. hdu6989 (莫队+单调栈+ST表)

    题意: 求l-r之间所有区间最大值最小值之和的期望,除法按照逆元来求; 题解: 看之前刚补的一道题目,那道题目跟这道题差不多,解释都在下面的链接中. [HNOI2016]序列 就是取余把人取傻了. # ...

  7. 洛谷P3245 [HNOI2016]大数 【莫队】

    题目 题解 除了\(5\)和\(2\) 后缀数字对\(P\)取模意义下,两个位置相减如果为\(0\),那么对应子串即为\(P\)的倍数 只用对区间种相同数个数\(x\)贡献\({x \choose 2 ...

  8. P3246 [HNOI2016]序列(查询l-r中所有区间的最小值之和)

    多校时做到了查询区间l-r中所有区间的最大值与最小之和的题目,有好多细节不太会处理,去看题解发现是一道差不多的原题,于是打算先把原题补一下. 题解: ST表+单调栈+莫队 看到计算区间最小值之和,不难 ...

  9. 洛谷P3628DTOJ1220 [APOI2010]特别行动队

    洛谷P3628&&DTOJ1220 [APOI2010]特别行动队 题目 题目描述 输入格式 输出格式 样例 样例输入 样例输出 数据范围与提示 题解 题目 题目描述 原题 你有一支由 ...

最新文章

  1. 排序算法之冒泡,选择,插入
  2. ecshop管理找不到index.php,前台出现找不到这样的目录,打不开某文件的提示
  3. php 根号2计算过程,根号2以及π的计算--关于无理数的畅想
  4. 20个it专业术语_DevSecOps这个术语是否必要?
  5. Android 读取Assets资源
  6. 红外条码扫描器的另类使用C#版
  7. pathway一些网站
  8. C# 处理应用程序减少内存占用
  9. python中如何将列表按列打印_如果列表只包含0,我如何在python中打印?
  10. origin刻度消失_使用Origin制作XRD图基本技巧,你get到了嘛?
  11. 台式计算机怎么关闭无线网络,台式机无线网卡被禁用了如何解决
  12. 移动应用前端h5框架汇总
  13. CPC是什么意思和CPM、CPV有什么不同?
  14. MySQL——数据库
  15. Anomaly Detection with partially Observed Anomalies论文笔记
  16. java web编程技术解题与实验指导_javaweb编程技术实验指导书
  17. Python 3.x 学习笔记
  18. markdown转VNode
  19. Linux内核模块it87出错
  20. Android开发知识体系,技术实现

热门文章

  1. php printf 0.2f,php printf()
  2. SpringSecurity分布式整合之分布式认证流程说明
  3. zookeeper的设计猜想-leader选举
  4. MyBatis 实际使用案例-一级标签
  5. Spring5的数据访问与集成
  6. 原始Junit测试Spring的问题
  7. MapReduce-流量统计求和-排序-JobMain代码和测试运行
  8. POI的入门:创建单元格设置数据
  9. 分隔线演练-增加多行分隔线函数的参数
  10. 二叉树的遍历实现-2(三级)