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. 【MM系列】SAP MM模块-组织结构第二篇

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP MM模块-组织结构第二篇   ...

  2. 【ABAP系列】SAP ABAP ALV设置背景图片

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP abap ALV设置背景图片 ...

  3. C#异步:AsyncCallback和IAsyncResult

    在线程池异步的执行委托,异步编程模型 msdn官方解释:https://msdn.microsoft.com/zh-cn/library/ms228972(VS.80).aspx 使用AsyncCal ...

  4. Git-第四篇廖雪峰Git教程学习笔记(3)远程仓库,克隆远端库

    1.本次连接的是gitHub仓库. 1>创建SSH Key. ssh-keygen -t rsa -C "youremail@example.com" lfy@lfy-PC ...

  5. git ssh key配置&解决git每次输入密码

    git ssh key配置&解决git每次输入密码:https://blog.csdn.net/qq_42817227/article/details/81415404

  6. 【Vue】通过自定义指令回顾 v-内置指令

    Vue.js 的各种指令(Directives)更加方便我们去数据驱动 DOM,例如 v-bind.v-on.v-model.v-if.v-for.v-once 等内置指令,这些指令的职责就是当表达式 ...

  7. Knight Moves (双向bfs)

    # 10028. 「一本通 1.4 例 3」Knight Moves [题目描述] 编写一个程序,计算一个骑士从棋盘上的一个格子到另一个格子所需的最小步数.骑士一步可以移动到的位置由下图给出. [算法 ...

  8. 关于.net的精彩对话(转)

    [序言]我想很多爱好软件编程的网友都像我一样,对微软推出的.net平台充满了好奇,但是看了相关的文档也是一头雾水,还好,Purple很幸运在QQ上遇到了一位.net高手,经过高手的一番教导,Purpl ...

  9. mysql中explain输出列之id的用法详解

    参考mysql5.7 en manual,对列id的解释: The SELECT identifier. This is the sequential number of the SELECT wit ...

  10. 使用控制台搭建vue-cli脚手架

    注意: 1.安装前您需要查看自己是否有node环境  检查:node - v 2.如果没有的话,需要先搭建好才能进行下一步操作 (参考:https://www.cnblogs.com/sylys/p/ ...