BZOJ.4540.[HNOI2016]序列(莫队/前缀和/线段树 单调栈 RMQ)
ST表的一二维顺序一定要改过来。
改了就rank1了哈哈哈哈。自带小常数没办法。
\(Description\)
给定长为\(n\)的序列\(A_i\)。\(q\)次询问,每次给定\(l,r\),求\(\sum\limits_{i=l}^r\sum\limits_{j=i}^r\min\{A_i,A_{i+1},...,A_j\}\)。
\(n,q\leq10^5\)。
\(Solution\)
莫队:
这种区间询问问题考虑一下莫队。
考虑移动右端点\(r\to r+1\)的时候怎么更新答案。
设\(r+1\)左边第一个比\(A_{r+1}\)小的数是\(A_p\),那么\(Ans\)+=\((r+1-p)*A_{r+1}\);同理再找到\(p\)右边第一个比\(A_p\)小的数\(A_{p'}\),\(Ans\)+=\((p-p')*A_p\);再找\(p'\)左边第一个比\(A_{p'}\)小的数......
这样怎么快速确定一个边界呢?
令\([l,r+1]\)的最小值是\(A_x\)(用\(RMQ\)得到),处理到\(x\)位置后,\(Ans\)+=\((x-l+1)*A_x\)就可以了。
而左边第一个比\(A_i\)小的数的位置可以单调栈维护出来,记为\(L_i\)。
因为每个数作为最小值时的区间是不会相交的,大概就是会形成一个树形结构。反正是可以用一个类似前缀和的东西维护,记为\(sum_i=sum_{L_i}+(i-L_i)*A_i\)(右端点在\(i\),左端点在\([1,i]\)时的答案)。
这样移动右端点\(r\to r+1\)的时候,\(Ans\)+=\(sum_{r+1}-sum_x+(x-l+1)*A_x\),就可以\(O(1)\)更新了。
对于左端点,还要再反着维护一个\(sum\)。
复杂度\(O(n\log n+n\sqrt{n})\)。
优化:
对于询问\([l,r]\),令\([l,r]\)的区间最小值为\(A_p\)。
那么对于左端点在\([l,p]\),右端点在\([p,r]\)的子区间,最小值都是\(A_p\),所以\(p\)的贡献就是\(A_p\times(p-l+1)\times(r-p+1)\)。
当子区间左右端点都在\((p,r]\)时,答案就是\(sum_r+sum_{r-1}+...+sum_{p+1}-sum_p\times(r-p)\)。
记\(s_i=\sum_{j=1}^isum_j\),那么答案就是\(s_r-s_p-sum_p\times(r-p)\)。
同理对于子区间左右端点在\([l,p)\)时,反着维护一个\(s\)。
预处理出来就可以在线\(O(1)\)回答询问了,复杂度\(O(n\log n)\)。
线段树:
这儿写的挺详细的,想再写一写。
这种\(\sum_{i=l}^r\sum_{j=i}^r\)的询问,把\([l,r]\)中的子区间写出来。比如拿样例来说,\(l=1,\ r=5\),每个子区间的最小值为:
\(1\ \ 1\ \ 1\ \ 1\ \ 3\\1\ \ 1\ \ 1\ \ 1\\2\ \ 2\ \ 4\\2\ \ 2\\5\)
当询问区间为\([l,r]\)时,答案就是第\(1\)行到第\(r\)行中,前\(l\sim r\)列的和。
也就是每次右端点由\(r-1\)移动到\(r\),是给区间\(i\sim r,\ 1\leq i\leq r\)分别加了一个值\(\min\limits_{j=i}^r\{a_j\}\),也就是上面的三角中多了第\(r\)行。
如果此时询问右端点就是\(r\),那答案就是\([l,r]\)此时的区间和。
观察每一行的区间加,每次\(A_r\)会覆盖\(r\)前面一段区间,然后就不变和上一次一样了。
而\(A_r\)会覆盖的区间就是\([p+1,r]\),\(p\)是\(r\)前面第一个比\(A_r\)小的位置,可以单调栈预处理。
也就是我们把\([p+1,r]\)的\(val\)改成\(A_r\),然后所有位置的\(sum\)+=\(val\)。
怎么维护呢?
记此时的区间\(val\)的和为\(sum\),区间长度为\(len\),要修改的值为\(v\)。不妨把\([l,r]\)的答案记作\(his\),也就是\([l,r]\)中所有点各历史版本的\(val\)的和。
那么修改就是:\(sum'=v*len\),更新\(his\)就是:\(his'=his+sum'\)(表示不理解为什么都写的\(+sum\)啊QAQ)。
要用线段树高效维护区间修改,就得能维护标记合并。
把转移用矩阵写出来,就是这样:$$\begin{bmatrix}len & sum & his \\end{bmatrix}
\begin{bmatrix}
1 & a & c \
0 & b & d \
0 & 0 & 1 \
\end{bmatrix}=\begin{bmatrix}len' & sum' & his' \\end{bmatrix}$$
维护\(a,b,c,d\)四个值。修改的参数就是\(a=v,b=c=d=0\),更新\(his\)的参数是\(d=1,a=b=c=0\),单位矩阵(初始化)是\(b=1,a=c=d=0\)。
虽然参数用不到\(c\),但是矩阵结合的时候会改变\(c\),但不会影响其他不维护的位置,所以要维护\(c\)。
这样利用矩阵的结合律,就可以维护标记合并了。
复杂度\(O(n\log n)\)。
这个实在懒得写了...没心情。(虽然我知道我几天就会给忘了)
莫队:
//13240kb 3876ms->2980ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <assert.h>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,INF=0x7fffffff;
int bel[N],A[N],sk[N],Log[N],pos[18][N];
LL suml[N],sumr[N],Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Quries
{
int l,r,id;
bool operator <(const Quries &x)const
{
return bel[l]==bel[x.l]?r<x.r:bel[l]<bel[x.l];
}
}q[N];
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now*f;
}
inline int Min(int x,int y)
{
return A[x]<=A[y]?x:y;
}
inline int Query(int l,int r)
{
int k=Log[r-l+1];
return Min(pos[k][l],pos[k][r-(1<<k)+1]);
}
void Init_ST(const int n)
{
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
pos[j][i]=Min(pos[j-1][i],pos[j-1][i+t]);
}
inline LL UpdL(int l,int r)
{
int p=Query(l,r);
return suml[l]-suml[p]+1ll*(r-p+1)*A[p];
}
inline LL UpdR(int l,int r)
{
int p=Query(l,r);
return sumr[r]-sumr[p]+1ll*(p-l+1)*A[p];
}
int main()
{
const int n=read(),Q=read(),size=sqrt(n);
for(int i=1; i<=n; ++i) bel[i]=i/size, A[i]=read(), pos[0][i]=i;
for(int i=1; i<=Q; ++i) q[i]=(Quries){read(),read(),i};
Init_ST(n), A[0]=-INF;
for(int i=1,top=0; i<=n; ++i)
{
while(A[sk[top]]>A[i]) --top;
sumr[i]=sumr[sk[top]]+1ll*(i-sk[top])*A[i], sk[++top]=i;
}
A[sk[0]=n+1]=-INF;
for(int i=n,top=0; i; --i)
{
while(A[sk[top]]>A[i]) --top;
suml[i]=suml[sk[top]]+1ll*(sk[top]-i)*A[i], sk[++top]=i;
}
std::sort(q+1,q+1+Q);
LL Now=0;
for(int i=1,l=1,r=0; i<=Q; ++i)
{
int ln=q[i].l,rn=q[i].r;
while(l>ln) Now+=UpdL(--l,r);//l的减 或 r的加 要先进行,否则就会出现l>r的情况 然后Query的时候就gg了
while(r<rn) Now+=UpdR(l,++r);
while(l<ln) Now-=UpdL(l++,r);
while(r>rn) Now-=UpdR(l,r--);
Ans[q[i].id]=Now;
}
for(int i=1; i<=Q; printf("%lld\n",Ans[i++]));
return 0;
}
优化:
//12444kb 700ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,INF=0x7fffffff;
int A[N],sk[N],Log[N],pos[18][N];
LL suml[N],sumr[N],sl[N],sr[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now*f;
}
inline int Min(int x,int y)
{
return A[x]<=A[y]?x:y;
}
inline int Query(int l,int r)
{
int k=Log[r-l+1];
return Min(pos[k][l],pos[k][r-(1<<k)+1]);
}
void Init_ST(const int n)
{
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1;
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
pos[j][i]=Min(pos[j-1][i],pos[j-1][i+t]);
}
int main()
{
const int n=read(),Q=read();
for(int i=1; i<=n; ++i) A[i]=read(), pos[0][i]=i;
Init_ST(n), A[0]=-INF;
for(int i=1,top=0; i<=n; ++i)
{
while(A[sk[top]]>A[i]) --top;
sumr[i]=sumr[sk[top]]+1ll*(i-sk[top])*A[i], sr[i]=sr[i-1]+sumr[i];
sk[++top]=i;
}
A[sk[0]=n+1]=-INF;
for(int i=n,top=0; i; --i)
{
while(A[sk[top]]>A[i]) --top;
suml[i]=suml[sk[top]]+1ll*(sk[top]-i)*A[i], sl[i]=sl[i+1]+suml[i];
sk[++top]=i;
}
for(int i=1; i<=Q; ++i)
{
int l=read(),r=read(),p=Query(l,r);
printf("%lld\n",1ll*A[p]*(p-l+1)*(r-p+1)+sr[r]-sr[p]-sumr[p]*(r-p)+sl[l]-sl[p]-suml[p]*(p-l));
}
return 0;
}
BZOJ.4540.[HNOI2016]序列(莫队/前缀和/线段树 单调栈 RMQ)的更多相关文章
- BZOj 4540: [Hnoi2016]序列 [莫队 st表 预处理]
4540: [Hnoi2016]序列 题意:询问区间所有子串的最小值的和 不强制在线当然上莫队啦 但是没想出来,因为不知道该维护当前区间的什么信息,维护前后缀最小值的话不好做 想到单调栈求一下,但是对 ...
- BZOJ 4540 [Hnoi2016]序列 | 莫队 详细题解
传送门 BZOJ 4540 题解 --怎么说呢--本来想写线段树+矩阵乘法的-- --但是嘛--yali的机房太热了--困--写不出来-- 于是弃疗,写起了莫队.(但是我连莫队都想不出来!) 首先用单 ...
- bzoj 4540: [Hnoi2016]序列 莫队
题目: 给定长度为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 ...
- [HNOI2016]序列(莫队,RMQ)
[HNOI2016]序列(莫队,RMQ) 洛谷 bzoj 一眼看不出来怎么用数据结构维护 然后还没修改 所以考虑莫队 以$(l,r-1) -> (l,r)$为例 对答案的贡献是$\Sigma_ ...
- 线段树+单调栈+前缀和--2019icpc南昌网络赛I
线段树+单调栈+前缀和--2019icpc南昌网络赛I Alice has a magic array. She suggests that the value of a interval is eq ...
- [BZOJ4540][HNOI2016]序列 莫队
4540: [Hnoi2016]序列 Time Limit: 20 Sec Memory Limit: 512 MB Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n ...
- BZOJ 4540 [Hnoi2016]序列 (单调栈 + ST表 + 莫队算法)
题目链接 BZOJ4540 考虑莫队算法. 这题难在$[l, r]$到$[l, r+1]$的转移. 根据莫队算法的原理,这个时候答案应该加上 $cal(l, r+1) + cal(l+1, r+1) ...
- 【BZOJ4540】[Hnoi2016]序列 莫队算法+单调栈
[BZOJ4540][Hnoi2016]序列 Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,a ...
- BZOJ.4542.[HNOI2016]大数(莫队)
题目链接 大数除法是很麻烦的,考虑能不能将其条件化简 一段区间[l,r]|p,即num[l,r]|p,类似前缀,记后缀suf[i]表示[i,n]的这段区间代表的数字 于是有 suf[l]-suf[r+ ...
随机推荐
- 数据恢复Winhex的核心理念
一.数据恢复就是寻找有价值的东西,其本质是数据定位,检索技术.存在的有迹可循,毁灭的无影无踪.这就譬如说,一本撕烂的书输的目录不见了,但是内容全在,我们就可以读到内容,但是内容不存在了,也就无法在找到 ...
- spring cloud Eureka注册中心集群搭建
1.创建springcloud-eureka maven项目 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0&quo ...
- 在Git.oschina.net中配置TortoiseGit使用sshkey,无需输入账号和密码
ssh的方式 git@oschina.com:用户名/版本库t.git 此篇文章针对于这种 黄海正在开发的项目位置 https://gitee.com/dslx/BigData.g ...
- java 解析域名得到host
// 形如https://www.baidu.com 或 www.baidu.com, 判断这两种情况,并解析前者去掉http头,传入domain host // 方案1:正则表达式 + URI解析方 ...
- BETA准备
过去存在的问题 文档问题 未能事先做好设计文档,且小程序与后端数据库接口存在问题 界面问题 原型图设计花的时间较少,小程序设计界面仍相对粗糙,前端成员css元素应用经验不足 小程序界面之间对接存在问题 ...
- 【转】git shell 命令大全
http://www.cnblogs.com/bugs/p/3384339.html 常用命令 git branch 查看本地所有分支 git status 查看当前状态 git commit 提交 ...
- js前端ajax提交list集合参数至后端
var orderNosList = new Array(); var rows = $("#dg_linkOrder").datagrid("getChecked&qu ...
- Codeforces 982E Billiard exgcd
Billiard 枚举终点, 对于每一个终点一共有四种周期的相遇方式, 枚举一下取最小的时间. #include<bits/stdc++.h> #define LL long long # ...
- input时间表单默认样式修改(input[type="date"])
一.时间选择的种类: HTML代码:选择日期:<input type="date" value="2018-11-15" /> 选择时间:<i ...
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 动态点分治 堆
原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ1095.html 题目传送门 - BZOJ1095 题意 有 N 个点,每一个点是黑色或者白色,一开始所 ...