洛谷-P9485 题解
写在前面:这是蒟蒻交的第一篇绿题题解(大祭),因为线性做法比较难想,本篇会着重讲述用 RMQ 问题求解,并尽可能用清晰明了的图片和简易的文字讲明白。
正文
最坏时间复杂度:
在求解之前,先让我们想个问题,如何求解积水格数?再简单点,对于每个
以
大家应该都听过木桶效应吧,
那么,上述问题“对于每个
显然这是可以用 DP 求解的,设
l[0]=0,l[1]=0;//求l for(register int i=2;i<=n;i++) if(a[i-1]>=a[l[i-1]]) l[i]=i-1; else l[i]=l[i-1]; r[n+1]=0,r[n]=0;//求r for(register int i=n-1;i>=1;i--) if(a[i+1]>=a[r[i+1]]) r[i]=i+1; else r[i]=r[i+1];
知道了左右两峰的高度,自然而然也就可以求每个
s=0; for(register int i=1;i<=n;i++){ p[i]=min(a[l[i]],a[r[i]]); if(p[i]-a[i]>0) w[i]=p[i]-a[i];//如果a[i]与左右两峰形成“凹”状 else w[i]=0;//否则不积水 s+=w[i]; }
请记住上面的变量符号,后面会用到。
好了,关于积水的问题求解完了,接下来就进入重点了。对于每个
第一类,
此时将
因此,有代码(
for(register int i=1;i<=n;i++) ans=min(ans,s-w[i]);
第二类,
如果我们分别枚举每个
设
那么如何求可以减少的积水格数呢?
以
综上:对于每个
那么,如果我们这样写,就可以愉快的 WA 了。
来,看看
这里就是 RMQ 问题了,因为是离线的,可以用 ST 表解决(当然你想用线段树也没人拦你,只是蒟蒻不太会QWQ)。
把上述结论的
但是!!!还没结束!!!看
所以还要在
这里只讨论了降低右峰的情况,左峰同理。
好啦,上这部分的 Code!
for(register int i=1;i<=n;i++){ if(p[i]>=a[i]){ //如果i是峰,不管如何降低a[l[i]]或a[r[i]]都无法增加排水格数 to=max(a[l[l[i]]],query(l[i]+1,i-1)); v[l[i]]+=(p[i]-max(to,a[i])>0? p[i]-max(to,a[i]):0);//讨论左峰 to=max(a[r[r[i]]],query(i+1,r[i]-1)); v[r[i]]+=(p[i]-max(to,a[i])>0? p[i]-max(to,a[i]):0);//讨论右峰 } }
因此,
for(register int i=1;i<=n;i++) ans=min(ans,s-v[i]);
两类情况讨论完毕,上总 Code!
#define by_wanguan #include<iostream> #include<cstring> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int N=1e6+7; ll l[N],r[N],a[N],T,n,v[N],p[N],w[N],s,ans,to; ll read(){ll x=0,w=1;char ch=0;while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch-'0');ch=getchar();}return x*w;} void write(ll x){int sta[24],top=0;if(x<0){putchar('-');x=-x;}do{sta[top++]=x%10;x/=10;}while(x);while(top)putchar(sta[--top]+'0');} /* l[i] i左边最高位置 r[i] i右边最高位置 a[i] i高度 p[i] i积水高度 v[i] i两侧峰削去可减少积水格数 w[i] i积水格数 */ //ST表,不会去P3865 ll lg2[N],pp[22],ma[N][21],len,lg,pl; inline void init(){ pp[0]=1; for(register int i=1;i<=20;i++) pp[i]=pp[i-1]*2; int cnt=0,last=2; for(register int i=2;i<N;i++){ if(i==last) cnt++,last*=2; lg2[i]=cnt; } } inline void solve(){ for(register int i=1;i<=lg2[n]+1;i++) for(register int j=1;j<=n;j++) ma[j][i]=max(ma[j][i-1],ma[min(j+pp[i-1],n)][i-1]); } inline int query(int l,int r){ if(l>r) return 0; if(l==0) l=1; len=r-l+1,lg=lg2[len],pl=pp[lg]; return max(ma[l][lg],ma[r-pl+1][lg]); } signed main(){ T=read(); init(); while(T--){ n=read(); for(register int i=1;i<=n;i++) a[i]=read(),ma[i][0]=a[i]; solve(); l[0]=0,l[1]=0; for(register int i=2;i<=n;i++) if(a[i-1]>=a[l[i-1]]) l[i]=i-1; else l[i]=l[i-1]; r[n+1]=0,r[n]=0; for(register int i=n-1;i>=1;i--) if(a[i+1]>=a[r[i+1]]) r[i]=i+1; else r[i]=r[i+1]; s=0; for(register int i=1;i<=n;i++){ p[i]=min(a[l[i]],a[r[i]]); if(p[i]-a[i]>0) w[i]=p[i]-a[i]; else w[i]=0; s+=w[i]; } for(int i=1;i<=n;i++) v[i]=0; for(register int i=1;i<=n;i++){ if(p[i]>=a[i]){ to=max(a[l[l[i]]],query(l[i]+1,i-1)); v[l[i]]+=(p[i]-max(to,a[i])>0? p[i]-max(to,a[i]):0); to=max(a[r[r[i]]],query(i+1,r[i]-1)); v[r[i]]+=(p[i]-max(to,a[i])>0? p[i]-max(to,a[i]):0); } } ans=s; for(register int i=1;i<=n;i++) ans=min(ans,s-w[i]), ans=min(ans,s-v[i]); write(ans),putchar('\n'); } }
后附
日志
v1.0 on 2023.07.31: 发布
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步