【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
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/AWCXV/p/7643975.html