防御准备

BZOJ

(N)个点只能放置守卫塔.求最小总花费.

分析:从前往后找太麻烦了,直接倒过来就行,即第一个点必须防止守卫塔,不放置守卫塔的点必须要在它的左边(前面)找到守卫塔.

(f[i])表示第i个点放置守卫塔的最小总花费,则有

(f[i]=min(f[j]+a[i]+sum_{k=j+1}^{i-1}k-j))

(sum[i]=sum[i-1]+i=frac{(i+1)*i}{2}),但是为了能够斜率优化,我们得把它拆成含有乘积的式子.

(sum_{k=j+1}^{i-1}k-j=sum[i-1]-sum[j]-(i-j-1)*j),(sum)数组含义与上面相同.

(f[i]=f[j]+a[i]+sum[i-1]-sum[j]-(i-j-1)*j)

(k<j)且j比k更优,即

(f[j]+a[i]+sum[i-1]-sum[j]-(i-j-1)*j<f[k]+a[i]+sum[i-1]-sum[k]-(i-k-1)*k)

整理一下上式得到,

(frac{f[j]+j+j^2-sum[j]-f[k]-k-k^2+sum[k]}{j-k}<i)

就可以进行斜率优化了.

(n)点不一定会放置守卫塔.

[ZJOI2007]仓库建设这道题很相似.

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
   int s=0,w=1;char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
   return s*w;
}
const int N=1000005;
int a[N],q[N];LL f[N],sum[N];
inline double Count(int j,int k){return 1.0*(f[j]+1ll*j*j+j-sum[j]-f[k]-1ll*k*k-k+sum[k])/(double)(j-k);}//j*j可能会爆int
int main(){
    int n=read();
    for(int i=1;i<=n;++i){
		a[n-i+1]=read();
		sum[i]=sum[i-1]+i;
    }
    int l=1,r=1;f[1]=a[1];q[1]=1;
    for(int i=2;i<=n;++i){
		while(l<r&&Count(q[l+1],q[l])<=i)l++;
		f[i]=f[q[l]]+a[i]+sum[i-q[l]-1];
		while(l<r&&Count(q[r],q[r-1])>=Count(i,q[r]))r--;
		q[++r]=i;
    }
    for(int i=1;i<=n;++i)f[n]=min(f[n],f[i]+sum[n-i]);//这里超级容易忽略掉
    printf("%lld
",f[n]);
    return 0;
}