【BZOJ 3156】防御准备
【链接】 链接
【题意】
【题解】
把a倒过来 设f[i]表示在i放一个防御塔的最小花费; 我们如果从j转移过来 就表示j+1..i-1这一段放人偶。 s[i] = 1 + 2 + ... + i; 则 $f[i] = fj + (s[i-1]-s[j]) -(i-1-j)*j + a[i]$ 做一下斜率优化就好。 因为我们第一段可能一开始没有放防御塔。 所以先假设在第n个位置放了一个防御塔。 再枚举最后一段没放防御塔的情况。【错的次数】
在这里输入错的次数【反思】
转移方程那里一开始写成s[i-1]-s[j]了。。。错误地认为是i+1..j这一段了。而实际上是i+1..j-1这一段。【代码】
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6;
int n,dl[N+10],h,t;
ll a[N+10],f[N+10],s[N+10];
double ju(int x,int y)
{
double fenzi = f[y]-s[y]+1LL*y*y+y-(f[x]-s[x]+1LL*x*x+x);
double fenmu = y-x;
return fenzi/fenmu;
}
int main()
{
//freopen("F:\\rush.txt","r",stdin);
scanf("%d",&n);
for (int i = n;i >= 1;i--) scanf("%lld",&a[i]);
for (int i = 1;i <= n;i++) s[i] = s[i-1] + i;
f[1] = a[1];
h = t = 1;
dl[1] = 1;
for (int i = 2;i <= n;i++)
{
while (h < t && ju(dl[h],dl[h+1]) < i) h++;
int j = dl[h];
f[i] = f[j] + (s[i-1]-s[j])-1LL*(i-1-j)*j + a[i];
while (h < t && ju(dl[t-1],dl[t]) > ju(dl[t],i)) t--;
dl[++t] = i;
}
ll ans = f[n];
for (int i = 1;i<=n-1;i++)
ans=min(ans,f[i]+s[n]-s[i]-1LL*(n-i)*i);
printf("%lld\n",ans);
return 0;
}