BZOJ3156: 防御准备

【传送门:BZOJ3156


简要题意:

  给出n个点,每个点对其进行两种操作:第一种将这个点变为特殊点,花费为a[i],第二种将这个点变为普通点,花费为右边最接近的特殊点与这个点的距离(比如说当前点为i点,右边最接近的点为j点,那么花费为(j-i)),这样就说明最右边的点必须为特殊点

  求出将每个点都进行操作的最小花费


题解:

  一开始想的是贪心,但是WA了,很懵,只好用DP

  因为逆推很麻烦,就将a数组翻转,这样的话找左边的最接近的特殊点就可以了

  得到方程:f[i]=min(f[j]+(i-j)*(i-j-1)/2+a[i])

  因为两个特殊点之间的所有普通点的左边最接近的特殊点就是j,所以总花费为1+2+3+...+(i-j-1)=(i-j)*(i-j-1)/2

  然后用斜率优化

  列出斜率方程为:(j1<j2<i)

  (2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1))/(j2-j1)<2*i

  然后做就好了

  PS:要加long long,注意i和j也要,因为j2^2,j1^2会爆int(这个问题WA了我七次,吐血)


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
LL f[1100000],a[1100000];
//f[i]=min(f[j]+(i-j-1)*(i-j)/2+a[i])
//f[i]=min(f[j]+(i-j-1)*(i-j)/2)+a[i]
/*
j1<j2<i
f[j2]+(i-j2-1)*(i-j2)/2<f[j1]+(i-j1-1)*(i-j1)/2
2*(f[j2]-f[j1])+(i-j2-1)*(i-j2)<(i-j1-1)*(i-j1)
2*(f[j2]-f[j1])+i*i-2*i*j2+j2^2-i+j2<i*i-2*i*j1+j1^2-i+j1
2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1)<2*i*j2-2*i*j1
2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1)<2*i*(j2-j1)
(2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1))/(j2-j1)<2*i
*/
LL slop(LL j1,LL j2)
{
    return (2*(f[j2]-f[j1])+(j2*j2-j1*j1)+(j2-j1))/(j2-j1);
}
int list[1100000];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=n;i>=1;i--) scanf("%lld",&a[i]);
    int head=1,tail=1;
    list[1]=1;f[1]=a[1];
    for(LL i=2;i<=n+1;i++)
    {
        while(head<tail&&slop(list[head],list[head+1])<2*i) head++;
        LL j=list[head];
        f[i]=f[j]+(i-j-1)*(i-j)/2+a[i];
        while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i)) tail--;
        list[++tail]=i;
    }
    printf("%lld
",min(f[n],f[n+1]));
    return 0;
}

 

原文地址:https://www.cnblogs.com/Never-mind/p/7928571.html