防御准备

BZOJ

题意:长度为\(N(N<=10^6)\)的序列,对于点i,要么花费\(a[i]\)放置守卫塔,要么花费\(j-i\)不放置守卫塔(其中j是点i右侧第一个守卫塔).显然第\(N\)个点只能放置守卫塔.求最小总花费.

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

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

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

其实后面那个累加式可以预处理出来\(sum[i-(j+1)]\),其中\(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\)

就可以进行斜率优化了.

但是还有一个超级容易被忽视的地方就是本题不能直接输出\(f[n]\),因为\(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\n",f[n]);
    return 0;
}

posted on 2019-06-13 15:32  PPXppx  阅读(136)  评论(0编辑  收藏  举报