题解-[HNOI2016]序列
题解-[HNOI2016]序列
给定 \(n\) 和 \(m\) 以及序列 \(a\{n\}\)。有 \(m\) 次询问,每次给定区间 \([l,r]\in[1,n]\),求
\[\sum_{l\le l'\le r'\le r}\min_{i=l'}^{r'}a_i
\]
数据范围:\(1\le n,m\le 10^5\),\(|a_i|\le 10^9\)。
蒟蒻要练习省选题,结果就遇到这道数据结构(好久没写数据结构题都忘光了)。结果正好遇到一道毒瘤题,于是蒟蒻来写篇题解。
这题是静态离线,令人想到 \(\texttt{ST}\) 表和莫队——真的就是他们。
将区间存下来排序,将左端点范围从小到大分 \(\sqrt n\) 份,左端点按份排序,右端点奇偶波浪排序。
friend int operator<(Moq x,Moq y){
if(cas[x.l]!=cas[y.l]) return x.l<y.l;
return (cas[x.l]&1)?x.r<y.r:x.r>y.r;
}
然后依次考虑排序后每一个区间询问,并通过上一个区间的答案递推,这就是莫队的思想。
然后看这题:
如何通过上一个答案递推呢?需要先知道边界端点的贡献。
即给定区间 \([l,r]\),\(l\) 端点的贡献\(=Ans[l,r]-Ans[l+1,r]\)。
比如下面给出一个序列 \(a\{n\}(n=10)\):
4 4 5 3 6 2 1 5 6 9
给定区间 \([l,r]=[3,9]\),即 \(a_l\sim a_r\) 为:
5 3 6 2 1 5 6
所以左端点 \((l=3,a_l=5)\) 的贡献应该为
\]
即
\]
\]
令人想起单调栈,然而不可能每次询问 \(\Theta(n)\) 跑一遍。所以可以预处理:
通过维护单调栈,求出对于每个 \(a_i\),\(lw_i(lw_i<i)\) 表示 \(i\) 左边第一个比 \(a_i\) 小的元素的下标;\(rw_i(rw_i>i)\) 表示 \(i\) 右边第一个比 \(a_i\) 小的元素的下标(如果左边不存在比 \(a_i\) 小的元素,\(lw_i=0\);如果右边不存在比 \(a_i\) 小的元素,\(rw_i=n+1\))。
这样的话,就可以维护一个前缀和 \(lsm_i\) 后缀和 \(rsm_i\),其中
\]
然后上面的 \(5+3+3+2\) 就可以通过后缀和相减得,求右端点贡献时则用前缀和相减。
因为对于区间 \([lw_i+1,i]\) 或 \([i,rw_i-1]\),\(a_i\) 为其最小元素。
void Side(){
lw.rz(n+7),rw.rz(n+7);
a[0]=-inf;
q.clear(),q.pb(0);
for(int i=1;i<=n;i++){
while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
lw[i]=q.back(),q.pb(i);
}
a[0]=0;
a[n+1]=-inf;
q.clear(),q.pb(n+1);
for(int i=n;i>=1;i--){
while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
rw[i]=q.back(),q.pb(i);
}
a[n+1]=0;
lsm.rz(n+7),rsm.rz(n+7);
for(int i=1;i<=n;i++) lsm[i]=lsm[lw[i]]+(lng)(i-lw[i])*a[i];
for(int i=n;i>=1;i--) rsm[i]=rsm[rw[i]]+(lng)(rw[i]-i)*a[i];
}
最后一个问题:\(1+1+1\) 部分怎么办?
因为右端点是随机的,所以如果直接把左端点的贡献当做 \(rsm_l-rsm_r\),必然不妥。
考虑到假设区间 \([l,r]\) 中 \(a_p\) 最小,那么必然
\]
所以算 \(1+1+1\) 部分可以通过维护静态区间最小值下标(\(\texttt{ST}\) 表)找到 \(p\),算出 \(a_p(r-p+1)\)。
然后正因为 \(a_p\) 是 \([l,r]\) 区间中最小的元素,所以 \([l,p-1]\) 段的左端点贡献也自然是 \(rsm_l-rsm_p\)。
所以对于区间 \([l,r]\),左端点贡献为
\]
右端点同理。
然后相邻两个区间之间就可以逐步转移了。
lng Mol(int l,int r){
int p=getmin(l,r);
return rsm[l]-rsm[p]+(lng)(r-p+1)*a[p];
}
lng Mor(int l,int r){
int p=getmin(l,r);
return lsm[r]-lsm[p]+(lng)(p-l+1)*a[p];
}
void runMo(){
int L=1,R=0;
for(int i=1;i<=m;i++){
while(L>qu[i].l) res+=Mol(--L,R);
while(R<qu[i].r) res+=Mor(L,++R);
while(L<qu[i].l) res-=Mol(L++,R);
while(R>qu[i].r) res-=Mor(L,R--);
ans[qu[i].I]=res;
}
}
时间复杂度 \(\Theta(m\sqrt n)\),空间复杂度 \(\Theta(n+m)\)。
Code
我这个蒟蒻垃圾真是傻傻讲不清楚,还是放代码吧(要开 \(\texttt{long long}\))。
代码有点长,于是蒟蒻划分了一下。
#include <bits/stdc++.h>
using namespace std;
//Start
#define lng long long
#define db double
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define rz resize
const int inf=0x3f3f3f3f;
const lng INF=0x3f3f3f3f3f3f3f3f;
//Data
int n,m;
vector<int> a;
//Side
vector<int> lw,rw,q;
vector<lng> lsm,rsm;
void Side(){
lw.rz(n+7),rw.rz(n+7);
a[0]=-inf;
q.clear(),q.pb(0);
for(int i=1;i<=n;i++){
while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
lw[i]=q.back(),q.pb(i);
}
a[0]=0;
a[n+1]=-inf;
q.clear(),q.pb(n+1);
for(int i=n;i>=1;i--){
while(q.size()&&a[q.back()]>=a[i]) q.pop_back();
rw[i]=q.back(),q.pb(i);
}
a[n+1]=0;
lsm.rz(n+7),rsm.rz(n+7);
for(int i=1;i<=n;i++) lsm[i]=lsm[lw[i]]+(lng)(i-lw[i])*a[i];
for(int i=n;i>=1;i--) rsm[i]=rsm[rw[i]]+(lng)(rw[i]-i)*a[i];//中间递推也要注意爆int
}
//ST
vector<int> lg;
vector<vector<int> > st;
int stcmp(int x,int y){
return a[x]<a[y]?x:y;
}
void buildst(){
lg.rz(n+7);
for(int i=2;i<=n;i++) lg[i]=lg[i-1]+((1<<(lg[i-1]+1))<=i?1:0); //lg[i]=(int)log(i)
st.rz(lg[n]+7);
for(int j=0;j<=lg[n];j++) st[j].rz(n+7);
for(int i=1;i<=n;i++) st[0][i]=i;
for(int j=1;j<=lg[n];j++)
for(int i=1;i<=n-(1<<(j-1));i++) st[j][i]=stcmp(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
int getmin(int l,int r){
int len=lg[r-l+1];
return stcmp(st[len][l],st[len][r-(1<<len)+1]);
}
//Moq
int sq;
lng res;
vector<int> cas;
vector<lng> ans;
struct Moq{
int l,r,I;
friend int operator<(Moq x,Moq y){
if(cas[x.l]!=cas[y.l]) return x.l<y.l;
return (cas[x.l]&1)?x.r<y.r:x.r>y.r;
}
};
vector<Moq> qu;
void buildMo(){
cas.rz(n+7),qu.rz(m+1);
sq=sqrt(n);
for(int i=1;i<=m;i++) scanf("%d%d",&qu[i].l,&qu[i].r),qu[i].I=i;
for(int i=1;i<=n;i++) cas[i]=(i-1)/sq+1;
sort(qu.begin(),qu.end());
}
lng Mol(int l,int r){
int p=getmin(l,r);
return rsm[l]-rsm[p]+(lng)(r-p+1)*a[p];
}
lng Mor(int l,int r){
int p=getmin(l,r);
return lsm[r]-lsm[p]+(lng)(p-l+1)*a[p];
}
void runMo(){
int L=1,R=0;
for(int i=1;i<=m;i++){
while(L>qu[i].l) res+=Mol(--L,R);
while(R<qu[i].r) res+=Mor(L,++R);
while(L<qu[i].l) res-=Mol(L++,R);
while(R>qu[i].r) res-=Mor(L,R--);
ans[qu[i].I]=res;
}
}
//Main
int main(){
scanf("%d%d",&n,&m);
a.rz(n+7);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ans.rz(m+7);
Side(),buildst(),buildMo(),runMo();
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}
祝大家学习愉快!
题解-[HNOI2016]序列的更多相关文章
- 【LG3246】[HNOI2016]序列
[LG3246][HNOI2016]序列 题面 洛谷 题解 60pts 对于每个位置\(i\),单调栈维护它往左第一个小于等于它的位置\(lp_i\)以及往右第一个小于它的位置\(rp_i\). 那么 ...
- [BZOJ4540][HNOI2016]序列 莫队
4540: [Hnoi2016]序列 Time Limit: 20 Sec Memory Limit: 512 MB Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n ...
- 【BZOJ4540】[Hnoi2016]序列 莫队算法+单调栈
[BZOJ4540][Hnoi2016]序列 Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,a ...
- BZOj 4540: [Hnoi2016]序列 [莫队 st表 预处理]
4540: [Hnoi2016]序列 题意:询问区间所有子串的最小值的和 不强制在线当然上莫队啦 但是没想出来,因为不知道该维护当前区间的什么信息,维护前后缀最小值的话不好做 想到单调栈求一下,但是对 ...
- 4540: [Hnoi2016]序列
4540: [Hnoi2016]序列 https://www.lydsy.com/JudgeOnline/problem.php?id=4540 分析: 莫队+RMQ+单调栈. 考虑加入一个点后,区间 ...
- BZOJ4540 Hnoi2016 序列 【莫队+RMQ+单调栈预处理】*
BZOJ4540 Hnoi2016 序列 Description 给定长度为n的序列:a1,a2,-,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,-,ar- ...
- [Bzoj4540][Hnoi2016] 序列(莫队 + ST表 + 单调队列)
4540: [Hnoi2016]序列 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1567 Solved: 718[Submit][Status] ...
- [HNOI2016]序列 CDQ+DP
[HNOI2016]序列 CDQ 链接 loj 思路 一个点最小变为l,最大变为r,不变的时候为v 那么j能在i前面就要满足. \(j<i\) \(r[j]<=v[i]\) \(v[j]& ...
- P6604 [HNOI2016]序列 加强版
*I. P6604 [HNOI2016]序列 加强版 摘自学习笔记 简单树论 笛卡尔树部分例题 I. 和 P6503 比较类似.我们设 \(f_i\) 表示全局以 \(i\) 结尾的子区间的最小值之和 ...
随机推荐
- TCP/IP模型简介和/etc/hosts文件说明
软件=协议的实现. IP决定了主机的位置.端口号决定了进程的位置. 两台主机上的通讯实际是两台主机上两个具体进程的通讯. TCP/IP模型分四层: TCP/IP模型:应用层---传输层----网络层- ...
- Maven项目关系
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),其中最重要的就是POM文件,可以指定项目类型,项目关系等信息,maven项目之间有三种关系. 依赖 ...
- MySQL给临时表分组后Max函数无效
有道练习题"取得平均薪水最高的部门的部门编号(至少给出两种解决方案)", 为什么我给临时表分组后Max函数就无效了?不分组就可以,但是无法查询到DEPTNO,MySQL版本8.0+ ...
- css万能清除原理
如果现在能有清理浮动的办法,但不至于在文档中多一个没有用的空标记,这时的效果是最好的!引入:after伪元素选择器,可以在指定的元素的内容添加最后一个子元素 .container:after{ } 如 ...
- pytest的setup和teardown
学过unittest的setup和teardown,前置和后置执行功能.pytest也有此功能并且功能更强大,今天就来学习一下吧. 用例运行级别: 模块级(setup_module/teardown_ ...
- 2016年第七届蓝桥杯【C++省赛B组】F、G、H、J 题解
F. 方格填数 #深搜 题意 有\(10\)个格子,填入0~9的数字.要求:连续的两个数字不能相邻.(左右.上下.对角都算相邻),求可能的填数方案数. +--+--+--+ | | | | +--+- ...
- 区块链学习1:Merkle树(默克尔树)和Merkle根
☞ ░ 前往老猿Python博文目录 ░ 一.简介 默克尔树(Merkle tree,MT)又翻译为梅克尔树,是一种哈希二叉树,树的根就是Merkle根. 关于Merkle树老猿推荐大家阅读<M ...
- 第三十四章、PyQt中的输入部件:QComboBox组合框功能详解
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.概述 Designer中输入工具部件中的Combo Box组合框与 ...
- 爬取网页内容后写入文件报错UnicodeEncodeError: 'gbk' codec can't encode的问题解决方案
老猿使用如下代码读取网页内容: req = urllib.request.Request(url=url,headers=header) text = urllib.request.urlopen(r ...
- 第15.5节 PyQt的历史沿革介绍
当朋友向我推荐PyQt时,老猿才知道有这样一个在Python下的开源的图形界面开发工具,当准备学习PyQt安装时,发现要安装sip.PyQt.PyQt-tools,然后还要进行相关配置.老猿很好奇为什 ...