洛谷题面传送门

一道笛卡尔树的 hot tea。

首先我们考虑一个非常 naive 的区间 DP:\(dp_{l,r}\) 表示区间 \([l,r]\) 的答案,那么我们考虑求出 \([l,r]\) 中最大值的位置所在的位置 \(p\),那么如果我们选取的 meeting 的位置 \(\le p\),那么显然 \([p+1,r]\) 部分的贡献都是 \(a_p\),\([l,p]\) 部分的总共先最小是 \(dp_{l,p}\),最优代价为 \(dp_{l,p}+a_p·(r-p)\),否则 \([l,p]\) 部分的贡献都是 \(a_p\),\([p+1,r]\) 部分的总贡献最小是 \(dp_{p+1,p}\),最优代价为 \(dp_{p+1,p}+a_p·(p-l+1)\),二者取 \(\min\) 即可。

直接做最优大概是 \(n^2\) 的,可以获得 19 分的好成绩(雾)。考虑优化,注意到这东西有点像笛卡尔树,因此我们考虑将笛卡尔树建出来。对于此题而言,由于数据范围允许 \(n\log n\) 通过,我们可以使用 \(n\log n\) 的 ST 表建树方法,不必采用那个严格线性的方式。我们考虑对于每组询问 \([L,R]\),找到 \([L,R]\) 区间中最大值所在的位置 \(p\),然后将整个询问拆成两个询问:\([L,p-1]\) 和 \([p+1,R]\)——为什么要这样干呢?原理与 P3246 [HNOI2016]序列 一致,因为这样我们就造出了两个区间,一个满足区间左边那个数大于等于区间中所有数,另一个则满足区间右边那个数大于等于区间中所有数(方便起见,对于后面的部分,我们可以将整个数组翻转一遍然后对翻转后的数组再做一遍这个操作,这样两部分就等价了,注意在后面一半建树部分有些小细节,所有值相同的最大值中,取下标最大的还是最小的要想清楚,不要想当然)这样一来,对于笛卡尔树上每个点 \(x\) 我们如果假设 \(x\) 子树内的点表示的区间左端点为 \(L_x\),右端点为 \(R_x\),那么对于满足“左边的数大于等于区间中所有数的区间 \([L,R]\)”,我们必然能够在笛卡尔树上找到某个点 \(x\),满足 \(L_x=L\),也就是说我们可以将每个询问挂在这个区间中所有点在笛卡尔树的 LCA,也就是 \([L,R]\) 最大值所在的位置表示的点上,然后离线一遍 DFS,DFS 的过程中扫描到点 \(x\) 时,用某种方式维护 \([L_x,L_x],[L_x,L_x+1],\cdots,[L_x,R_x]\) 的 DP 值,这样即可查出答案。

那么怎么维护这样些 DP 值呢?我们开一棵线段树,下标为 \(r\) 的位置实时维护右端点为 \(r\) 的区间的 DP 值。那么假设我们遍历到点 \(x\),\([L_x,R_x]\) 最大值所在的位置为 \(p\),那么我们先遍历 \([L_x,p-1],[p+1,R_x]\),那么对于 \([L_x,p-1]\) 中的位置,其 DP 值肯定不会发生变化,合并的时候就不用管它,对于 \(dp_{L_x,p}\),由于区间 \([L_x,p]\) 最大值就是 \(a_p\),因此我们肯定只能令 meeting 的位置 \(\le p\),此时 \(dp_{L_x,p}=dp_{L_x,p-1}+a_p·(p-L_x+1)\),在线段树上查询 \(p-1\) 位置的值然后做出对应修改即可。

比较棘手的是区间 \([p+1,R_x]\)。对于区间 \([p+1,R_x]\) 中的某个 \(v\),根据上面朴素的 DP 有 \(dp_{L_x,v}=\min(dp_{L_x,p}+(v-p)·a_p,dp_{p+1,v}+(p-L_x+1)·a_p)\),不难发现 \(\min\) 左右两边都是递增的,并且右边的部分增长速度严格慢于左边,因此我们考虑二分找出最右边的位置 \(pos\),满足 \(dp_{L_x,p}+(pos-p)·a_p\le dp_{p+1,pos}+(p-L_x+1)·a_p\),然后令 \([p+1,v]\) 位置中的 DP 值先清零,然后对于所有 \(i\in[p+1,v]\),DP 值假设 \((dp_{L_x,p}-p·a_p)+i·a_p\),\([v+1,p]\) 位置上的值整体加上 \((p-L_x+1)·a_p\),那么怎么找到这个位置 \(pos\) 呢?直接二分是 2log 的,不过注意到如果我们令 \(A=dp_{L_x,p}-p·a_p,B=a_p,C=(p-L_x+1)·a_p)\),那么我们等价于找到最右边的位置满足 \(A+Bi\le C+f(i)\),其中 \(f(i)\) 表示线段树上下标为 \(i\)​ 的位置上的值,这个一脸线段树二分的样子,线段树上二分一下即可做到 1log。

时间复杂度 \((n+q)\log n\)。

const int MAXN=7.5e5;
const int LOG_N=20;
int n,qu,a[MAXN+5];
pii qp[MAXN+5];
struct solver{
pii st[MAXN+5][LOG_N+2];
pii query(int l,int r){
int k=31-__builtin_clz(r-l+1);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int ncnt=0,rt=0,ch[MAXN+5][2],md[MAXN+5];
void build_cat(int &k,int l,int r){
if(l>r) return;if(!k) k=++ncnt;md[k]=abs(query(l,r).se);
// printf("%d %d %d\n",l,r,md[k]);
build_cat(ch[k][0],l,md[k]-1);build_cat(ch[k][1],md[k]+1,r);
}
void init(){
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=max(st[j][i-1],st[j+(1<<i-1)][i-1]);
build_cat(rt,1,n);
}
vector<pii> qv[MAXN+5];
void add_query(int l,int r,int id){
if(l>r) return;int ps=abs(query(l,r).se);
// printf("+ %d %d %d %d\n",l,r,id,ps);
qv[ps].pb(mp(r,id));
}
struct node{int l,r;ll val,lz0,lz1,t0;} s[MAXN*4+5];
void pushup(int k){s[k].val=s[k<<1|1].val;}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void make0(int k){s[k].t0=1;s[k].val=s[k].lz0=s[k].lz1=0;}
void tag(int k,ll v0,ll v1){
s[k].val+=v0+1ll*s[k].r*v1;
s[k].lz0+=v0;s[k].lz1+=v1;
}
void pushdown(int k){
if(s[k].t0){make0(k<<1);make0(k<<1|1);s[k].t0=0;}
if(s[k].lz0||s[k].lz1){
tag(k<<1,s[k].lz0,s[k].lz1);
tag(k<<1|1,s[k].lz0,s[k].lz1);
s[k].lz0=s[k].lz1=0;
}
}
void addrng(int k,int l,int r,ll v0,ll v1){
if(l>r) return;
if(l<=s[k].l&&s[k].r<=r) return tag(k,v0,v1),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) addrng(k<<1,l,r,v0,v1);
else if(l>mid) addrng(k<<1|1,l,r,v0,v1);
else addrng(k<<1,l,mid,v0,v1),addrng(k<<1|1,mid+1,r,v0,v1);
pushup(k);
}
void modify(int k,int p,ll x){
if(s[k].l==s[k].r) return s[k].val=x,void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
(p<=mid)?modify(k<<1,p,x):modify(k<<1|1,p,x);
pushup(k);
}
int walk(int k,int l,int r,ll lft,int dlt,ll rit){//the rightmost index i satisfying lft+dlt*i<=rit+f(i)
if(s[k].l==s[k].r) return (lft+1ll*dlt*s[k].l<=rit+s[k].val)?s[k].l:(s[k].l-1);
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return walk(k<<1,l,r,lft,dlt,rit);
else if(l>mid) return walk(k<<1|1,l,r,lft,dlt,rit);
else{
if(lft+1ll*dlt*mid<=rit+s[k<<1].val) return walk(k<<1|1,mid+1,r,lft,dlt,rit);
else return walk(k<<1,l,mid,lft,dlt,rit);
}
}
void makezero(int k,int l,int r){
if(l>r) return;
if(l<=s[k].l&&s[k].r<=r) return make0(k),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) makezero(k<<1,l,r);
else if(l>mid) makezero(k<<1|1,l,r);
else makezero(k<<1,l,mid),makezero(k<<1|1,mid+1,r);
pushup(k);
}
ll query_pt(int k,int p){
if(s[k].l==s[k].r) return s[k].val;
pushdown(k);int mid=s[k].l+s[k].r>>1;
return (p<=mid)?query_pt(k<<1,p):query_pt(k<<1|1,p);
}
ll res[MAXN+5];
void solve(int k,int l,int r){
// printf("solving %d %d %d\n",k,l,r);
if(!k) return;
if(!ch[k][0]&&!ch[k][1]){
modify(1,l,st[l][0].fi);
for(pii p:qv[l]) res[p.se]=st[l][0].fi;
return;
} int mid=md[k];
solve(ch[k][0],l,mid-1);solve(ch[k][1],mid+1,r);
ll val=(!ch[k][0])?st[mid][0].fi:(query_pt(1,mid-1)+st[mid][0].fi);
modify(1,mid,val);
// printf("%d %d %d %lld\n",l,r,mid,val);
if(mid<r){
int pos=walk(1,mid+1,r,val-1ll*st[mid][0].fi*mid,st[mid][0].fi,1ll*st[mid][0].fi*(mid-l+1));
makezero(1,mid+1,pos);addrng(1,mid+1,pos,val-1ll*st[mid][0].fi*mid,st[mid][0].fi);
addrng(1,pos+1,r,1ll*(mid-l+1)*st[mid][0].fi,0);
} for(pii p:qv[mid]) res[p.se]=query_pt(1,p.fi);
}
void work(){build(1,1,n);solve(rt,1,n);}
} fw,bk;
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) fw.st[i][0]=mp(a[i],i);
for(int i=1;i<=n;i++) bk.st[i][0]=mp(a[n-i+1],-i);
fw.init();bk.init();
for(int i=1;i<=qu;i++){
scanf("%d%d",&qp[i].fi,&qp[i].se);++qp[i].fi;++qp[i].se;
int ps=fw.query(qp[i].fi,qp[i].se).se;
// printf("ps=%d\n",ps);
fw.add_query(ps+1,qp[i].se,i);
bk.add_query(n-ps+2,n-qp[i].fi+1,i);
} fw.work();bk.work();
// for(int i=1;i<=qu;i++) printf("%lld %lld\n",fw.res[i],bk.res[i]);
for(int i=1;i<=qu;i++){
ll r1=fw.res[i],r2=bk.res[i];
int ps=fw.query(qp[i].fi,qp[i].se).se;
printf("%lld\n",min(r1+1ll*a[ps]*(ps-qp[i].fi+1),r2+1ll*a[ps]*(qp[i].se-ps+1)));
}
return 0;
}

洛谷 P5044 - [IOI2018] meetings 会议(笛卡尔树+DP+线段树)的更多相关文章

  1. 洛谷 P5897 - [IOI2013]wombats(决策单调性优化 dp+线段树分块)

    题面传送门 首先注意到这次行数与列数不同阶,列数只有 \(200\),而行数高达 \(5000\),因此可以考虑以行为下标建线段树,线段树上每个区间 \([l,r]\) 开一个 \(200\times ...

  2. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  3. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]

    传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...

  4. 洛谷P4254 [JSOI2008]Blue Mary开公司(李超线段树)

    题面 传送门 题解 李超线段树板子 具体可以看这里 //minamoto #include<bits/stdc++.h> #define R register #define fp(i,a ...

  5. 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  6. 【洛谷 P2464】[SDOI2008]郁闷的小J(线段树)

    题目链接 这题我很久之前用分块写过,没写出来.. 今天又看到了,于是下决心把这题做出来. 这次我用线段树写的,直接对每本书的编号Hash一下然后离散化然后各建一棵线段树,维护当前编号在某个位置有没有书 ...

  7. 洛谷P4770 [NOI2018]你的名字(后缀自动机+线段树)

    传送门 我有种自己根本没学过SAM的感觉……最后还是抄了老半天的题解…… 首先,对$S$和每一次的$T$都建一个SAM 先考虑一下$l=1,r=\left| S \right|$的情况 设$lim_i ...

  8. 洛谷P4364 [九省联考2018]IIIDX 【线段树】

    题目 [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在 ,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款 ...

  9. 洛谷P4364 [九省联考2018]IIIDX(线段树)

    传送门 题解看得……很……迷? 因为取完一个数后,它的子树中只能取权值小于等于它的数.我们先把权值从大到小排序,然后记$a_i$为他左边(包括自己)所有取完他还能取的数的个数.那么当取完一个点$x$的 ...

随机推荐

  1. Head First Python 代码和实例下载

    http://python.itcarlow.ie/resources.html

  2. javascript-jquery-文档处理

    一.移动元素 1.append():向每个匹配元素的内部追加内容.例如:$("选择器1").qppend("选择器2"):将会匹配选择器2的元素,移动到匹配选择 ...

  3. 【UE4 设计模式】单例模式 Singleton Pattern

    概述 描述 保证一个类只有一个实例 提供一个访问该实例的全局节点,可以视为一个全局变量 仅在首次请求单例对象时对其进行初始化. 套路 将默认构造函数设为私有, 防止其他对象使用单例类的 new运算符. ...

  4. 新型活跃Mozi样本分析报告

    基本信息 对象 值 文件名 Photo.scr 文件类型 PE32 executable for MS Windows (GUI) Intel 80386 32-bit 文件大小 6271259 by ...

  5. Java:并发笔记-08

    Java:并发笔记-08 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 7. 共享模型之工具-1 7.1 线程池 1. 自定义线程池 步骤1:自定义拒绝策略接 ...

  6. the Agiles Scrum Meeting 3

    会议时间:2020.4.11 21:30 1.每个人的工作 今天已完成的工作 yjy:基本实现广播功能的前端 issues:小组任务1-增量开发组 wjx:基本实现注销功能的后端 issues:小组任 ...

  7. 鸿蒙轻内核M核的故障管家:Fault异常处理

    摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十八 Fault异常处理& ...

  8. 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(二)

    一.基于邻接表的Dijkstra算法 如前一篇文章所述,在 Dijkstra 的算法中,维护了两组,一组包含已经包含在最短路径树中的顶点列表,另一组包含尚未包含的顶点.使用邻接表表示,可以使用 BFS ...

  9. PyPi到底是什么?pypi有啥作用?PyPi和pip有何渊源?

    转载:https://blog.csdn.net/weixin_42139375/article/details/82711201 可能有很多刚入行不久的朋友们,每天都在用pip 命令install ...

  10. Python import urllib2 ImportError: No module named 'urllib2'

    python3 import urllib2 import urllib2 ImportError: No module named 'urllib2' python3.3里面,用urllib.req ...