https://www.luogu.org/problemnew/show/P5044

题解

这种关于最大值或者最小值的问题,可以往笛卡尔树的方面想。

先考虑一个朴素的\(dp\),设\(dp[l][r]\)表示\(l\sim r\)的答案,转移是:

\[dp[l][r]=min(dp[l][p-1]+a[p]*(r-p+1),dp[p+1][r]+a[p]*(p-l+1))
\]

\[p=min(a[l]\sim a[r])
\]

这个东西乍一看好像没什么优化空间,但是由于我们有笛卡尔树,所以我们可以加一些限制。

比如说我们强制让询问区间的一个端点为笛卡尔树上的某个端点。

然后考虑这时的转移:

\[dp[l][i]=min(dp[l][p-1]+a[p]*(i-p+1),dp[p+1][i]+(p-l+1)*a[p])
\]

我们观察到随着\(i\)的向右移动,前面的部分每次都会增加\(a[p]\),后面的的部分每次增加都不会超过\(a[p]\)(根据笛卡尔树的性质)。

于是我们可以发现准备更新的这段区间被分为两部分,一部分是区间加等差数列,一部分是加常数,这个可以用线段树上二分完成。

对于一般的区间,我们直接可以把它拆成两种情况:选在最大值左边或者选在最大值右边,这样端点就有保障了,最后我们把两种情况的答案取个\(min\)就可以了。

Warning

打标记的时候要注意,因为下放的时候标记会清空,所以区间的值直接累加,不要直接更新。

代码

#include<bits/stdc++.h>
#define ls cnt<<1
#define rs cnt<<1|1
#define N 750009
using namespace std;
typedef long long ll;
ll h[N],ans[N],ans1[N],ans2[N];
int n,lo[N],p[23][N],ch[N][2],Q,rot;
vector<int>vec1[N],id1[N],vec2[N],id2[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
inline int _max(int a,int b){return h[a]<h[b]?b:a;}
inline int f(int x){return n-x+1;}
inline int RMQ(int l,int r){
int loo=lo[r-l+1];
return _max(p[loo][l],p[loo][r-(1<<loo)+1]);
}
struct node{
ll val1,val2,k,b;
int tag;
}tr[N<<2];
inline void cover(int cnt,int l,int r,ll k,ll b){
tr[cnt].k=k;tr[cnt].b=b;
tr[cnt].val1=k*l+b;tr[cnt].val2=k*r+b;
tr[cnt].tag=1;
}
inline void add(int cnt,int l,int r,ll k,ll b){
tr[cnt].k+=k;tr[cnt].b+=b;
tr[cnt].val1+=k*l+b;
tr[cnt].val2+=k*r+b;//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(!tr[cnt].tag)tr[cnt].tag=2;
}
inline void pushdown(int cnt,int l,int r){
int mid=(l+r)>>1;
if(tr[cnt].tag){
if(tr[cnt].tag==1)cover(ls,l,mid,tr[cnt].k,tr[cnt].b),cover(rs,mid+1,r,tr[cnt].k,tr[cnt].b);
else add(ls,l,mid,tr[cnt].k,tr[cnt].b),add(rs,mid+1,r,tr[cnt].k,tr[cnt].b);
tr[cnt].k=tr[cnt].b=tr[cnt].tag=0;
}
}
inline void build(int &cnt,int l,int r){
if(l>r){cnt=0;return;}
cnt=RMQ(l,r);
build(ch[cnt][0],l,cnt-1);
build(ch[cnt][1],cnt+1,r);
}
ll query(int cnt,int l,int r,int x){
if(l==r)return tr[cnt].val1;
int mid=(l+r)>>1;
pushdown(cnt,l,r);
if(mid>=x)return query(ls,l,mid,x);
else return query(rs,mid+1,r,x);
}
void clear(int cnt,int l,int r){
tr[cnt].k=tr[cnt].val1=tr[cnt].val2=tr[cnt].b=tr[cnt].tag=0;
if(l==r)return;
int mid=(l+r)>>1;
clear(ls,l,mid);clear(rs,mid+1,r);
}
void upd(int cnt,int l,int r,int x,ll y){
if(l==r){
add(cnt,l,r,0,y);
return;
}
pushdown(cnt,l,r);
int mid=(l+r)>>1;
if(mid>=x)upd(ls,l,mid,x,y);
else upd(rs,mid+1,r,x,y);
tr[cnt].val1=tr[ls].val1;tr[cnt].val2=tr[rs].val2;
}
void work(int cnt,int l,int r,int L,int R,ll k1,ll b1,ll b2){
if(l>=L&&r<=R){
if(k1*l+b1>=tr[cnt].val1+b2){
// cout<<l<<" "<<r<<" "<<tr[cnt].val1<<endl;
add(cnt,l,r,0,b2);
return;
}
if(k1*r+b1<=tr[cnt].val2+b2){
cover(cnt,l,r,k1,b1);
return;
}
}
int mid=(l+r)>>1;
pushdown(cnt,l,r);
if(mid>=L)work(ls,l,mid,L,R,k1,b1,b2);
if(mid<R)work(rs,mid+1,r,L,R,k1,b1,b2);
tr[cnt].val1=tr[ls].val1;tr[cnt].val2=tr[rs].val2;
}
void solve(int now,int l,int r,int tag){
ll num=h[now];
if(ch[now][0])solve(ch[now][0],l,now-1,0),num+=query(1,1,n,now-1);
if(ch[now][1])solve(ch[now][1],now+1,r,1);
upd(1,1,n,now,num);
if(now!=r){
ll k1=h[now],b1=num-h[now]*now,b2=h[now]*(now-l+1);
work(1,1,n,now+1,r,k1,b1,b2);
}
if(tag){
for(int i=0;i<vec1[l].size();++i){
int x=vec1[l][i],idd=id1[l][i];
ans[idd]=query(1,1,n,x);
}
}
}
int main(){
n=rd();Q=rd();
for(int i=1;i<=n;++i)h[i]=rd(),p[0][i]=i;
for(int i=1;(1<<i)<=n;++i)
for(int j=1;j+(1<<i)-1<=n;++j)p[i][j]=_max(p[i-1][j],p[i-1][j+(1<<i-1)]);
for(int i=2;i<=n;++i)lo[i]=lo[i>>1]+1;
for(int i=1;i<=Q;++i){
int l=rd()+1,r=rd()+1;
int mid=RMQ(l,r);
ans1[i]=1ll*(mid-l+1)*h[mid];
if(mid+1<=r)vec1[mid+1].push_back(r),id1[mid+1].push_back(i);
ans2[i]=1ll*(r-mid+1)*h[mid];
if(l<=mid-1)vec2[f(mid-1)].push_back(f(l)),id2[f(mid-1)].push_back(i);
}
build(rot,1,n);
solve(rot,1,n,1);
for(int i=1;i<=Q;++i)ans1[i]+=ans[i],ans[i]=0;
clear(1,1,n);
reverse(h+1,h+n+1);
for(int i=1;(1<<i)<=n;++i)
for(int j=1;j+(1<<i)-1<=n;++j)p[i][j]=_max(p[i-1][j],p[i-1][j+(1<<i-1)]);
for(int i=1;i<=n;++i){
vec1[i]=vec2[i],id1[i]=id2[i];
ch[i][0]=ch[i][1]=0;
}
build(rot,1,n);
solve(rot,1,n,1);
for(int i=1;i<=Q;++i)ans2[i]+=ans[i];
for(int i=1;i<=Q;++i)
printf("%lld\n",min(ans1[i],ans2[i]));
return 0;
}

[IOI2018] meetings 会议的更多相关文章

  1. 洛谷 P5044 - [IOI2018] meetings 会议(笛卡尔树+DP+线段树)

    洛谷题面传送门 一道笛卡尔树的 hot tea. 首先我们考虑一个非常 naive 的区间 DP:\(dp_{l,r}\) 表示区间 \([l,r]\) 的答案,那么我们考虑求出 \([l,r]\) ...

  2. UOJ#410. 【IOI2018】会议

    传送门 首先可以设 \(f[l][r]\) 表示 \([l,r]\) 的答案 设 \(x\) 为区间 \([l,r]\) 的最大值的位置,那么 \(f[l][r] = min(f[l][x-1]+h[ ...

  3. [IOI2018]会议——分治+线段树

    题目链接: [IOI2018]meetings 题目大意:有$n$座山峰,每座山峰有一个高度,有$q$次询问,每次需要确定一个开会山峰使$[l,r]$所有山峰上的人都前往开会山峰,一个山峰的人去开会的 ...

  4. WC2019 题目集

    最近写的一些 WC2019 上讲的一些题.还是怕忘了,写点东西记录一下. LOJ2983 「WC2019」数树 题意 本题包含三个问题: 问题 0:已知两棵 \(n\) 个节点的树的形态(两棵树的节点 ...

  5. github帐户和仓库的创建

    sign up is registration and sign in is logging in for "in" is to enter an existing account ...

  6. 中国计算机学会CCF推荐国际学术会议

    中国计算机学会推荐国际学术会议 (计算机系统与高性能计算) 一.A类 序号 会议简称 会议全称 出版社 网址 1 ASPLOS Architectural Support for Programmin ...

  7. CCF推荐国际学术会议

    类别如下计算机系统与高性能计算,计算机网络,网络与信息安全,软件工程,系统软件与程序设计语言,数据库.数据挖掘与内容检索,计算机科学理论,计算机图形学与多媒体,人工智能与模式识别,人机交互与普适计算, ...

  8. 中国计算机学会CCF推荐国际学术期刊会议(最新版)

    中国计算机学会推荐国际学术期刊会议 2014年12月,中国计算机学会(CCF)启动新一轮<)计算机体系结构/高性能计算/存储系统: )计算机网络:)网络与信息安全:)软件工程/系统软件/程序设计 ...

  9. 401 WebEx会议教程一 —— 参加会议

    · WebEx会议教程一 —— 参加会议 参加他人发起的会议 1.  在邀请邮件中,接受对方的会议邀请: 2.  一般在WebEx会议开始前15分钟时,邮箱客户端会提醒您 (如下图类似提示) 3.   ...

随机推荐

  1. Golang 调用 aws-sdk 操作 S3对象存储

    Golang 调用 aws-sdk 操作 S3对象存储 前言 因为业务问题,要写一个S3对象存储管理代码,由于一直写Go,所以这次采用了Go,Go嘛,快,自带多线程,这种好处就不用多说了吧. 基础的功 ...

  2. X509格式的证书校验(基于GMSSL2019-06-15版本)

    实现X509格式证书的链式校验 // cert_public.cpp : Defines the exported functions for the DLL application. // #inc ...

  3. ora-01033,ORA-16038

    ORA-01033: ORACLE initialization or shutdown in progress 1.进入CMD,执行set ORACLE_SID=fbms,确保连接到正确的SID:2 ...

  4. 剑指offer(1):数组

    1 写作计划 最近在看<剑指offer>,发现自己有很多的数据结构与算法的基础知识要复习,<好书一起读(131):让写作更好>中提到用写作倒逼阅读,我很是赞同.所以,计划以&l ...

  5. VGA时序及其原理(转载)

    显示器扫描方式分为逐行扫描和隔行扫描:逐行扫描是扫描从屏幕左上角一点开始,从左像右逐点扫描,每扫描完一行,电子束回到屏幕的左边下一行的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信 ...

  6. python nginx+uwsgi+WSGI 处理请求详解

    https://blog.csdn.net/a519640026/article/details/76157976 请求从 Nginx 到 uwsgi 到 django 交互概览 作为python w ...

  7. TensorFlow学习笔记7-深度前馈网络(多层感知机)

    深度前馈网络(前馈神经网络,多层感知机) 神经网络基本概念 前馈神经网络在模型输出和模型本身之间没有反馈连接;前馈神经网络包含反馈连接时,称为循环神经网络. 前馈神经网络用有向无环图表示. 设三个函数 ...

  8. 设置IIS的gzip

    如果服务器iis 中没有配置动态压缩的话,在性能中选项中配置. 设置成功之后:

  9. 12 (H5*) JS第二天 流程控制:顺序结构、分支结构、循环结构

    目录 1:一元运算符 2:流程控制 3:分支之if语句 4:分支之if-else语句 5:分支语句之三元运算符 6:if和else if语句 7:switch-case语句 8:while循环 9:d ...

  10. c语言秋季作业1

    1:你对软件工程专业或者计算机科学与技术专业了解是怎样? answer:据我上网了解软件工程是一门研究用工程化方法构建和维护有效的.实用的和高质量的软件的学科.它涉及程序设计语言.数据库.软件开发工具 ...