【bzoj3156】 防御准备

http://www.lydsy.com/JudgeOnline/problem.php?id=3156 (题目链接)

题意

  给出n个防御节点,每个节点有两种选择,可以花费a[i]建立一个防御塔,或者放置一个木偶,木偶的花费为到右端第一个防御塔的距离。求最少花费。

Solution

  容易写出dp方程:$${f[i]=Min(f[j]+frac{(i-j)*(i-j-1)}{2})}$$

  其中${f[i]}$表示在${i}$处放置防御塔,前${i}$个节点已经完成防御所需要的最少花费。

  易证决策单调性,划出斜率式:$${i>=frac{(2*f[j]+j+j*j)-(2*f[k]+k+k*k)}{2*(j-k)}}$$

  其中${j<k<i}$。

细节

  斜率里面${j*j}$以及${k*k}$记得开long long,用一个错的程序拍了好久→_→。。

代码

// bzoj3156
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define MOD 100000000
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=1000010;
LL a[maxn],q[maxn],n;
LL f[maxn];

double slope(LL j,LL k) {
	return ((2.0*f[j]+j+j*j)-(2.0*f[k]+k+k*k))/(2.0*(j-k));
}
int main() {
	scanf("%lld",&n);
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	int l=1,r=1;q[1]=0;
	for (int i=1;i<=n;i++) {
		while (l<r && i>=slope(q[l],q[l+1])) l++;
		f[i]=f[q[l]]+(i-q[l])*(i-q[l]-1)/2+a[i];
		while (l<r && slope(q[r-1],q[r])>slope(q[r],i)) r--;
		q[++r]=i;
	}
	//for (int i=1;i<=n;i++) printf("%d : %lld
",i,f[i]);
	printf("%lld",f[n]);
	return 0;
}

  

原文地址:https://www.cnblogs.com/MashiroSky/p/6005994.html