[bzoj3156]防御准备

题目大意:给定一个长为$n$的序列,每个点需要放置一个守卫塔或一个木偶,在第$i$个点放置守卫塔的代价为$a_i$,放置木偶的代价为$j-i$,$j$为$i$右边第一个守卫塔,求最小代价。

题解:$O(n^2)$的$DP$显然,$ f_i=\min\limits_{j < i}\{f_j+a_i+1+2+\dots + (i - j - 2) + (i - j - 1)\}$

$$\begin{align*}
f_i&=\min\limits_{j < i}\{f_j+\frac{(i - j)(i - j - 1)}{2} + a_i\}\\
&=\min\limits_{j < i}\{f_j+\frac{i^2-ij-i+ij+j^2+j}{2}+a_i\}
\end{align*}$$

$$把关于i的部分提出来$$

$$f_i=\min\limits_{j < i}\{f_j+\frac{j^2+j}{2}+ij\} + a_i+\frac{i^2-i}{2}$$

$$令y_i=f_i+\frac{i^2+i}{2}$$

$$f_i=\min\limits_{j < i}\{y_j+ij\} + a_i+\frac{i^2-i}{2}$$

$$考虑a和b两个决策,若a<b且b比a优$$

$$\therefore y_b-ib<y_a-ia$$

$$\Rightarrow \frac{y_b-y_a}{b-a}<i$$

然后乱搞就好了

卡点:

 

C++ Code:

#include <cstdio>
#define int long long
#define maxn 1000010
using namespace std;
int n, a[maxn], f[maxn];
int q[maxn], h, t;
int y(int x) {return f[x] + (x * (x + 1) >> 1);}
double slope(int a, int b) {
	return 1.0 * (y(b) - y(a)) / (b - a);
}
signed main() {
	scanf("%lld", &n);
	h = t = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		while (h < t && slope(q[h], q[h + 1]) <= i) h++;
		int tmp = q[h];
		f[i] = y(tmp) - i * tmp + a[i] + (i * (i - 1) >> 1);
		while (h < t && slope(q[t - 1], q[t]) >= slope(q[t], i)) t--;
		q[++t] = i;
	}
	printf("%lld\n", f[n]);
	return 0;
}

  

posted @ 2018-08-12 16:51  Memory_of_winter  阅读(180)  评论(0编辑  收藏  举报