poj 3709 斜率优化

状态定义很简单,
然后经过一堆花里胡哨的运算,就可以算出方程
f[j]-sum[j]+a[j+1]*j = a[j+1]*i-sum[i]+f[i]
y(j) = f[j]-sum[j]+a[j+1]*j

然后经过一堆花里胡哨的证明就可以发现y(j), i, sum[i]都是单增的,然后就可以找出斜率优化的两个条件之一,
更优解比较
(y(q[l + 1]) - y(q[l])) <=i * (a[q[l + 1] + 1] - a[q[l] + 1])
(通过解方程:f[i] (从q[l+1]转移) <= f[i] (从q[l]转移)的方程得出)

斜率单调的比较
(a[now + 1] - a[q[r] + 1])(y(q[r]) - y(q[r - 1])) >=(a[q[r] + 1] - a[q[r - 1] + 1])(y(now) - y(q[r]))

最后是代码

#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#include <stack>

#define INF 0x3f3f3f3f
#define IMAX 2147483646
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int

using namespace::std;

const int maxn = 211111;

ll a[511111], sum[511111], n, k, t;
ll f[511111], now, q[511111], l, r;
#define y(j) (f[j] + a[j + 1] * j - sum[j])

int main() {
	scanf("%lld", &t);
	while (t--) {
		scanf("%lld%lld", &n, &k);
		for (int i = 1; i <= n; i++)
			scanf("%lld", a + i), sum[i] = sum[i - 1] + a[i];
		l = r = 1;
		q[1] = k;
		for (int i = k; i <= n; i++) {
			if (i < k * 2) { f[i] = sum[i] - i * a[1]; continue; }
			while (l < r && (y(q[l + 1]) - y(q[l])) <=
				i * (a[q[l + 1] + 1] - a[q[l] + 1]))l++;
			f[i] = f[q[l]] + sum[i] - sum[q[l]] - a[q[l] + 1] * (i - q[l]);
			now = i - k + 1;
			while (l < r && (a[now + 1] - a[q[r] + 1])*(y(q[r]) - y(q[r - 1])) >=
				(a[q[r] + 1] - a[q[r - 1] + 1])*(y(now) - y(q[r])))r--;
			q[++r] = now;
		}
		printf("%lld\n", f[n]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值