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. 应用安全_WTS-WAF绕过

    Access 检测: ?id=+AND+= ?id=+AND+=2 绕过: sqlmap.py -u http://www.xx.com/project.asp?id=29 --tables --ta ...

  2. 浅谈vue单页面seo问题

    最近做项目的时候,被要求做seo,由于项目已经开发完毕,且只需首页做seo,所以考虑再三,决定用prerender-spa-plugin结合vue-meta-info来实现首页的seo.如果你的页面是 ...

  3. Apache httpclient拦截器对请求进行签名

    Apahce httpclient 提供HttpRequestInterceptor和HttpResponseInterceptor两种拦截器分别处理请求和响应数据,下面讲一下如何对http请求进行拦 ...

  4. 如何配置属于自己的Git账户

    如何配置属于自己的Git账户:https://jingyan.baidu.com/article/3d69c55123e556f0cf02d730.html

  5. [CCPC-Wannafly & Comet OJ 夏季欢乐赛(2019)]飞行棋

    题目链接:https://www.cometoj.com/contest/59/problem/E?problem_id=2714 求期望并且一堆转移基本上就是期望dp了(叉腰 照常的设dp[i]表示 ...

  6. 洛谷P2460 [SDOI2007]科比的比赛(题解)(贪心+搜索)

    科比的比赛(题解)(贪心+搜索) 标签:算法--贪心 阅读体验:https://zybuluo.com/Junlier/note/1301158 贪心+搜索 洛谷题目:P2460 [SDOI2007] ...

  7. (四:NIO系列) Java NIO Selector

    出处:Java NIO Selector 1.1. Selector入门 1.1.1. Selector的和Channel的关系 Java NIO的核心组件包括: (1)Channel(通道) (2) ...

  8. hasattr() getattr() setattr() 函数使用详解??

    hasattr(object, name)函数: 判断一个对象里面是否有name属性或者name方法,返回bool值,有name属性(方法)返回True,否则返回False. **注意:name要使用 ...

  9. 14、前端知识点--Vue生命周期浅析

    vue生命周期 每个Vue实例或组件从创建到显示再到废弃的过程就是vue的生命周期.很多时候我们希望能在这个过程中执行一些操作,于是就有了生命周期钩子. 生命周期钩子函数允许我们在实例不同阶段执行各种 ...

  10. ISC2016训练赛 phrackCTF--findkey

    测试文件:https://static2.ichunqiu.com/icq/resources/fileupload/phrackCTF/REVERSE/findkey 1.准备 获得信息 Pytho ...