P5044-[IOI2018] meetings 会议【dp,笛卡尔树,线段树二分】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P5044


1|1题目大意

给出一个长度为n的序列h,定义dis(x,y)=max{hi}(xiy)

q次询问给出一个区间[L,R],找到一个x[L,R],最小化i=LRdis(i,x)的值。

1n,q750000,1hi109


1|2解题思路

先考虑暴力的做法,记fl,r表示区间[l,r]的答案,那么我们找出[l,r]的任意一个最大值的位置x,要么是左边跨过这个值要么是右边跨过这个值,也就是

fl,r=min{fl,x1+(rx+1)×hx,fx+1,r+(xl+1)×hx}

考虑怎么优化这个转移,因为关系到这个区间的最大值,所以我们考虑在笛卡尔树上计算答案。

对于每个询问[L,R],我们先找到这个区间的最大值x把它拆分成[L,x][x,R]的询问。

先考虑形如[x,R]这一部分的询问,因为这一部分都满足最大值在最左边。

对于每个笛卡尔树上的节点mid,记它的区间[l,r],我们考虑维护所有的fl,x其中x[l,r]

那么我们怎么转移这个东西

  • 对于x[l,mid1]fl,x代表的意义不变。
  • 对于x=midfl,mid=fl,mid1+hmid,因为mid是最高点,在这个点聚合显然不合适。
  • 对于x[mid+1,r]fl,x=min{fl,mid+hmid×(xmid),fmid+1,x+hmid×(midl+1)}

麻烦的是后面那个东西,我们考虑在线段树上维护所有的f?,x?根据处理到的节点改变)。

然后发现fl,mid+hmid×(xmid)根据x的变化稳定增加hmid,而fmid+1,x+hmid×(midl+1)这个玩意每次的增量是fmid+1,xfmid+1,x1,显然是不会超过hmid的。

所以选择它们两个的决策之间存在一个交点,我们可以二分这个位置。考虑这个加等差序列和区间推平操作都是要在线段树上搞的,所以我们直接顺手在线段树上二分就好了,因为这个的原因我们要记录一下左端点和右端点的权值。

时间复杂度:O( (n+q)logn )


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define ll long long using namespace std; const ll N=760000; struct node{ ll r,w,id; }; ll n,m,lg[N],h[N],f[N][20],ans[N]; ll ql[N],qr[N];vector<node> q[N]; struct SegTree{ #define M N<<2 ll w[M],k[M],c[M],wl[M],wr[M]; void Clear(){ memset(w,0,sizeof(w)); memset(wl,0,sizeof(wl)); memset(wr,0,sizeof(wr)); memset(k,0,sizeof(k)); memset(c,0,sizeof(c)); } void Setw(ll x,ll val){ w[x]=wl[x]=wr[x]=val; k[x]=c[x]=0;return; } void Setk(ll x,ll val){ wl[x]+=val;wr[x]+=val; k[x]+=val;return; } void Downdata(ll x,ll L,ll R){ if(w[x]>0)Setw(x*2,w[x]),Setw(x*2+1,w[x]),w[x]=-1; if(k[x])Setk(x*2,k[x]),Setk(x*2+1,k[x]),k[x]=0; if(c[x]){ ll mid=(L+R)>>1;k[x*2+1]+=c[x]*(mid-L+1); wr[x*2]+=c[x]*(mid-L);wl[x*2+1]+=c[x]*(mid-L+1); wr[x*2+1]+=c[x]*(R-L);c[x*2]+=c[x];c[x*2+1]+=c[x]; c[x]=0; } return; } void Change(ll x,ll L,ll R,ll l,ll r,ll val){ if(L==l&&R==r){Setw(x,val);return;} ll mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)Change(x*2,L,mid,l,r,val); else if(l>mid)Change(x*2+1,mid+1,R,l,r,val); else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val); wl[x]=wl[x*2];wr[x]=wr[x*2+1];return; } void Update(ll x,ll L,ll R,ll l,ll r,ll ks,ll cs){ if(L==l&&R==r){Setk(x,ks);c[x]+=cs;wr[x]+=(R-L)*cs;return;} ll mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)Update(x*2,L,mid,l,r,ks,cs); else if(l>mid)Update(x*2+1,mid+1,R,l,r,ks,cs); else Update(x*2,L,mid,l,mid,ks,cs),Update(x*2+1,mid+1,R,mid+1,r,ks,cs); wl[x]=wl[x*2];wr[x]=wr[x*2+1];return; } ll Ask(ll x,ll L,ll R,ll pos){ if(L==R)return wr[x]; ll mid=(L+R)>>1;Downdata(x,L,R); if(pos<=mid)return Ask(x*2,L,mid,pos); return Ask(x*2+1,mid+1,R,pos); } void Solve(ll x,ll L,ll R,ll l,ll r,ll k1,ll b1,ll b2){ if(L==l&&R==r){ if(b1>=wl[x]+b2) {Setk(x,b2);return;} if(k1*(R-L)+b1<=wr[x]+b2){ Setw(x,b1); wr[x]+=k1*(R-L);c[x]+=k1; return; } } ll mid=(L+R)>>1;Downdata(x,L,R); if(r<=mid)Solve(x*2,L,mid,l,r,k1,b1,b2); else if(l>mid) Solve(x*2+1,mid+1,R,l,r,k1,b1,b2); else Solve(x*2,L,mid,l,mid,k1,b1,b2), Solve(x*2+1,mid+1,R,mid+1,r,k1,b1+k1*(mid-l+1),b2); wl[x]=wl[x*2];wr[x]=wr[x*2+1];return; } #undef M }T; ll RMQ(ll l,ll r){ ll z=lg[r-l+1]; l=f[l][z];r=f[r-(1<<z)+1][z]; return (h[l]<h[r])?r:l; } void solve(ll L,ll R){ if(L>R)return; ll x=RMQ(L,R); solve(L,x-1); solve(x+1,R); for(ll i=0;i<q[x].size();i++) ans[q[x][i].id]=min(ans[q[x][i].id],q[x][i].w+T.Ask(1,1,n,q[x][i].r)); ll f=(x>L)?T.Ask(1,1,n,x-1):0; f+=h[x];T.Change(1,1,n,x,x,f); if(x<R)T.Solve(1,1,n,x+1,R,h[x],f+h[x],(x-L+1)*h[x]); return; } signed main() { scanf("%lld%lld",&n,&m); for(ll i=2;i<=n;i++)lg[i]=lg[i>>1]+1; for(ll i=1;i<=n;i++) scanf("%lld",&h[i]),f[i][0]=i; memset(ans,0x3f,sizeof(ans)); for(ll j=1;(1<<j)<=n;j++) for(ll i=1;i+(1<<j)-1<=n;i++){ ll a=f[i][j-1],b=f[i+(1<<j-1)][j-1]; f[i][j]=(h[a]<h[b])?b:a; } for(ll i=1;i<=m;i++){ scanf("%lld%lld",&ql[i],&qr[i]); ql[i]++;qr[i]++;ll x=RMQ(ql[i],qr[i]); q[x].push_back((node){qr[i],(x-ql[i]+1)*h[x],i}); } solve(1,n); reverse(h+1,h+1+n); for(ll i=1;i<=n;i++)q[i].clear(); for(ll j=1;(1<<j)<=n;j++) for(ll i=1;i+(1<<j)-1<=n;i++){ ll a=f[i][j-1],b=f[i+(1<<j-1)][j-1]; f[i][j]=(h[a]<h[b])?b:a; } for(ll i=1;i<=m;i++){ ql[i]=n-ql[i]+1;qr[i]=n-qr[i]+1; swap(ql[i],qr[i]);ll x=RMQ(ql[i],qr[i]); q[x].push_back((node){qr[i],(x-ql[i]+1)*h[x],i}); } T.Clear();solve(1,n); for(ll i=1;i<=m;i++) printf("%lld\n",ans[i]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16119444.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-04-08 P5012-水の数列【并查集,RMQ】
2021-04-08 P4234-最小差值生成树【LCT】
点击右上角即可分享
微信分享提示