bzoj2650. 积木
作者:互联网
一眼看上去不太可做,先找结论。
本题的核心思路:如果一个位置一边比它大,一边比它小,且确定了两边不动,那么提升它混乱值没有减小。
重点在于两边都不动,所以考虑 \(i,j\) 是不动的,中间都是动的,发掘一下性质。
首先 \(\min(h_i,h_j)>\max_{k=j+1}^{i-1}h_k\),因为中间如果有大于等于 \(\min(h_i,h_j)\) 的,它们不提升肯定比提升优。
既然中间的要小于两边的,那么它们提升之后肯定也不会超过两边。有了这个,可以推出中间的在提升之后一定变为同一个值。proof:因为左右两边是最大,所以中间在全部变为同一值前肯定存在一个结构:\(h_{k-1}\le h_k< h_{k+1}\),且 \(h_k\) 至少提升 \(1\),但是如果 \(h_{k-1}\) 不跟上混乱值不会减小,所以 \(h_{k-1}\) 必须跟上。重复以上步骤,最终中间会变为同一个值。
设中间最后变成的值为 \(x\),显然 \(\min(h_i,h_j)\ge x\ge \max_{k=j+1}^{i-1}h_k\)。\(x\) 取什么最优就是一个二次函数求最值,可以 \(\mathcal{O}(1)\) 完成。
考虑 DP,\(f_i\) 表示考虑了前 \(i\) 个,第 \(i\) 个值不动的答案,显然可以枚举 \(j\) 转移,时间 \(\mathcal{O}(n^2)\),但是有限制条件 \(\min(h_i,h_j)>\max_{k=j+1}^{i-1}h_k\),根据某场 ZR 模拟赛,这样的碗装结构只有 \(\mathcal{O}(n)\) 个(其实就差不多是单调栈出栈次数),可以用单调栈找出,时间复杂度 \(\mathcal{O}(n)\)。
对于统计答案,我们可以在开头结尾加一个 INF,且它们不参与混乱值的计算,以上结论分析一下还是对的。
#include<iostream>
#include<stdio.h>
#include<ctype.h>
#include<math.h>
#define int long long
#define N 1000005
using namespace std;
inline int read(){
int x=0,f=0; char ch=getchar();
while(!isdigit(ch)) f|=(ch==45),ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
int n,c,h[N],s[N],ss[N],f[N],st[N],top;
inline int calc(int i,int j,int l,int r){
int A=i-j-1;
int B=-2*(s[i-1]-s[j])-(i==n+1?0:c)-(j==0?0:c);
int C=(ss[i-1]-ss[j])+c*((i==n+1?0:h[i])+(j==0?0:h[j]));
int p=round(-(double)B/double(2*A)),x;
if(l<=p && p<=r) x=p;
else if(p<l) x=l;
else x=r;
return A*x*x+B*x+C;
}
signed main(){
n=read(),c=read();
h[0]=h[n+1]=N;
for(int i=1;i<=n;++i){
h[i]=read();
s[i]=h[i]+s[i-1];
ss[i]=h[i]*h[i]+ss[i-1];
}
st[++top]=0;
for(int i=1;i<=n+1;++i){
if(2<=i && i<=n) f[i]=f[i-1]+c*abs(h[i]-h[i-1]);
else f[i]=f[i-1];
while(top && h[i]>=h[st[top]]){
top--;
if(top){
int j=st[top],k=st[top+1];
f[i]=min(f[i],f[j]+calc(i,j,h[k],min(h[i],h[j])));
}
}
st[++top]=i;
}
cout<<f[n+1];
return 0;
}
标签:bzoj2650,ch,min,int,top,积木,mathcal,include 来源: https://www.cnblogs.com/xzzduang/p/16407498.html