【BZOJ 1911】【APIO 2010】特别行动队

http://www.lydsy.com/JudgeOnline/problem.php?id=1911

夏令营里斜率优化的例题,我调了一晚上,真是弱啊。

先推公式吧($sum_i$表示$x_1 \dots x_i$的和):

$$①f(i)=f(j)+a(sum_i -sum_j)^2 +b(sum_i -sum_j)+c$$

$$②f(i)=f(k)+a(sum_i -sum_k)^2 +b(sum_i -sum_k)+c$$

①和②分别表示从j和k这两个位置的转移过程,且满足$0≤j<k<i$

然后假设②比①更优,则②的等号右边减去①的等号右边大于0

$$f(k)-f(j)+a(sum_i -sum_k)^2 -a(sum_i -sum_j)^2 +b(sum_i -sum_k)  -b(sum_i -sum_j)>0$$

把平方算出来后相同的项消去得到:

$$f(k)-f(j)+2asum_i(sum_j - sum_k)-a(sum_j^2 - sum_k^2)+b(sum_j -sum_k)>0$$

又因为$sum_j -sum_k < 0$,所以两边同时除以$sum_j -sum_k$:

$$\frac{f(k)-f(j)}{sum_j -sum_k}+2asum_i +b-a(sum_j + sum_k)<0$$

移项后通分:

$$2asum_i +b<\frac{[f(k)+asum_k^2]-[f(j)+asum_j^2]}{sum_k -sum_j}$$

$2asum_i +b$是单调递减的,这样就化成了一个斜率优化的式子,对于一个位置$t$,可以把它看成二维平面上坐标为$(sum_t,f(t)+asum_t^2)$的点,用双端队列维护一个这些点的下凸壳进行转移,时间复杂度$O(n)$

夏令营讲题时自己推式子推错了!!!斯巴达!!!!!!!!!好久才发现。式子改正过来后对拍还是错,后来发现改的太急了忘加了两个括号斯巴达!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

为了A掉这道题耗了一晚上,全是脑残和手残造成的,已无力吐槽_(:з」∠)_

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000003;
typedef long long ll;
int in() {
	int k = 0, fh = 1; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar())
		if (c == '-') fh = -1;
	for(; c >= '0' && c <= '9'; c = getchar())
		k = (k << 3) + (k << 1) + c - '0';
	return k * fh;
}

ll f[N], sum[N], key;
int n, a, b, c, q[N];

bool cmp(int x, int y) {
	return f[y] + sum[y] * sum[y] * a  - f[x] - sum[x] * sum[x] * a > key * (sum[y] - sum[x]);
}
bool cmpk(int x, int y, int z) {
	return (f[z] + sum[z] * sum[z] * a - f[y] - sum[y] * sum[y] * a) * (sum[y] - sum[x])
		 > (f[y] + sum[y] * sum[y] * a - f[x] - sum[x] * sum[x] * a) * (sum[z] - sum[y]);
}

int main() {
	n = in(); a = in(); b = in(); c = in();
	sum[0] = 0;
	for(int i = 1; i <= n; ++i)
		sum[i] = in(), sum[i] += sum[i - 1];
	
	ll qu;
	int head = 0, tail = 1, t;
	f[0] = 0;
	f[1] = sum[1] * sum[1] * a + sum[1] * b + c;
	q[0] = 0; q[1] = 1;
	for(int i = 2; i <= n; ++i) {
		key = sum[i] * a * 2 + b;
		while (head < tail && cmp(q[head], q[head + 1])) ++head;
		t = q[head]; qu = sum[i] - sum[t];
		f[i] = f[t] + qu * qu * a + qu * b + c;
		while (head < tail && cmpk(q[tail - 1], q[tail], i)) --tail;
		q[++tail] = i;
	}
	
	printf("%lld\n", f[n]);
	return 0;
}

( ̄▽ ̄")不过最后还是A掉了233

NOI 2017 Bless All
posted @ 2016-07-20 21:48  abclzr  阅读(223)  评论(0编辑  收藏  举报