题目大意:
搭积木是xx最喜欢的游戏之一。xx有n块高低不同的积木,她将它们排成一列。xx希望积木看起来尽可能的整齐,她将相邻两块积木高度之差的绝对值之和乘上系数c定义为积木序列的混乱值,显然,混乱值越小越好。xx可以通过调整积木的高度使其混乱值变小,她可以花费t^2的代价,往某块积木上再搭一块高为t(t为任意自然数)的积木,在同一块积木上只能搭一次。
xx想考考你,混乱值与花费之和的最小值是多少呢?
思路:
考虑高度的dp显然很不优秀,所有我们要想办法去除高度的影响。
假设最终序列中[l,r]中,只有l,r的高度没有发生变化,那么一个结论是[l+1,r-1]的高度必定都是相同的,简单地证明:
如果最后地序列中高度不全部相同,那么我们可以把[l+1,r-1]中最高的减少1单位,那么答案肯定是会更优的。
于是我们可以考虑一个
n
2
n^2
n2的dp:
d
p
[
i
]
=
min
(
d
p
[
j
]
+
∑
k
=
j
+
1
i
−
1
(
x
−
h
[
k
]
)
2
+
(
h
[
i
]
+
h
[
j
]
−
x
×
2
)
×
c
)
dp[i]=\min(dp[j]+\sum_{k=j+1}^{i-1}(x-h[k])^2+(h[i]+h[j]-x\times 2)\times c)
dp[i]=min(dp[j]+k=j+1∑i−1(x−h[k])2+(h[i]+h[j]−x×2)×c)
后面的式子可以二次函数来
O
(
1
)
O(1)
O(1)求解最小值。
(
i
−
j
−
1
)
×
x
2
−
2
×
(
∑
k
=
j
+
1
i
−
1
h
[
k
]
+
c
)
×
x
+
(
∑
k
=
j
+
1
i
−
1
h
2
[
k
]
+
(
h
[
i
]
+
h
[
j
]
)
×
c
)
(i-j-1)\times x^2-2\times(\sum_{k=j+1}^{i-1}h[k]+c)\times x+(\sum_{k=j+1}^{i-1}h^2[k]+(h[i]+h[j])\times c)
(i−j−1)×x2−2×(k=j+1∑i−1h[k]+c)×x+(k=j+1∑i−1h2[k]+(h[i]+h[j])×c)
于是考虑怎么优化枚举,不难发现只有当
max
k
=
j
+
1
i
−
1
h
[
k
]
≤
min
(
h
[
i
]
,
h
[
j
]
)
\max_{k=j+1}^{i-1}h[k]\leq \min(h[i],h[j])
maxk=j+1i−1h[k]≤min(h[i],h[j])时,转移才有可能更优。比每一个点高的点只会转移一次,于是我们可以维护一个单调递减的栈,每一次在栈中转移比它矮的点,同时将元素一个一个弹出,直到转移到比它高的为止。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("tsinsen1315.in","r",stdin);
freopen("tsinsen1315.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e6+10;
const ll inf=LLONG_MAX>>1;
int n;
ll c,h[maxn],sum[maxn],sum2[maxn],dp[maxn],ans=inf;
stack<int>stk;
void init(){
read(n); read(c);
REP(i,1,n)read(h[i]);
REP(i,1,n){
sum[i]=sum[i-1]+h[i];
sum2[i]=sum2[i-1]+h[i]*h[i];
}
}
void transfer(int j,int i,ll Min){
if(i==j+1)return;
ll a=(i-j-1),Max=min(h[j],h[i]);
ll b=-2*(sum[i-1]-sum[j])-((j!=0)+(i!=n+1))*c;
ll d=sum2[i-1]-sum2[j]+(h[j]*(j!=0)+h[i]*(i!=n+1))*c;
ll x=round(-1.0*b/2/a);
x=min(max(x,Min),Max);
dp[i]=min(dp[i],dp[j]+a*x*x+b*x+d);
}
void work(){
memset(dp,63,sizeof(dp));
h[0]=h[n+1]=inf;
dp[0]=0; stk.push(0);
REP(i,1,n+1){
dp[i]=dp[i-1]+(i!=1 && i!=n+1)*abs(h[i]-h[i-1])*c;
ll Max=0;
while(stk.size()!=1 && h[stk.top()]<=h[i]){
transfer(stk.top(),i,Max);
Max=h[stk.top()];
stk.pop();
}
transfer(stk.top(),i,Max);
stk.push(i);
}
printf("%lld\n",dp[n+1]);
}
int main(){
File();
init();
work();
return 0;
}