P4072 [SDOI2016]征途

额...很难想象这题我调了好长时间....

首先我们不难得出答案就是

minmx2(x)2min{m*\sum{x^2}-(\sum{x})^2}

最小化中间那个平方

做法一是斜率优化

细节有:

  1. 斜率不要带负数单调递减......
  2. 注意double的用法
  3. 直接使用滚动数组即可,除了求值完全基于上个数组

做法二再加上wqs二分凑齐凸优化

细节:判断的时候别用你妈的eps

f[n].se>=m的时候就统计答案

还用longlong

斜率优化
code:


#include<bits/stdc++.h>
#define db double
#define ll long long
using namespace std;
const int MAXN = 3e3 + 7;
int n, m;
int a[MAXN], sum[MAXN];
int que[MAXN];
ll f[MAXN], g[MAXN];

inline ll calc(int x, int y) {
	return g[y] + 1ll * (sum[x] - sum[y]) * (sum[x] - sum[y]);
}

inline db k(int x) {
	return 2.0 * sum[x];
}

//y f_x+sum_x^2
inline ll gety(int b) {
	return g[b] + 1ll * sum[b] * sum[b];
}
//x sum_x
//k 2*sum_k
//b f_y-sum_y^2
inline db getslope(int x, int y) {
	return (gety(y) - gety(x)) / (sum[y] - sum[x]);//del(y)/del(x);
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		sum[i] = sum[i - 1] + a[i];
		g[i] = sum[i] * sum[i];
		// printf("%d?\n", sum[i]);
	}
	int h = 1, t = 1;
	for(int j = 2; j <= m; ++j) {
		h = 1, t = 1;
		que[h] = j - 1;
		for(int i = 1; i <= n; ++i) {
			f[i] = 0;
		}
		for(int i = j; i <= n; ++i) {
			while(h < t && getslope(que[h], que[h + 1]) < k(i)) {
				// printf("%d %d %d %lf %lf\n", i, que[h], que[h + 1], getslope(que[h], que[h + 1]), k(i));
				++h;
			}
			// printf("%d %d %d %lf %lf\n", i, que[h], que[h + 1], getslope(que[h], que[h + 1]), k(i));
			// printf("in->%d %d?%d %d\n", i, que[h], h, t);
			f[i] = calc(i, que[h]);
			// printf("dedaole :%d?%d %d?\n", f[i], g[que[h]], sum[i] - sum[que[h]]);
			// if(h < t)
			// 	printf("now tb is :%lf?%lf\n", getslope(que[h], que[h + 1]), k(i));
			while(h < t && getslope(que[t], que[t - 1]) > getslope(que[t], i))
				--t;
			que[++t] = i;
		}
		for(int i = 1; i <= n; ++i) {
			g[i] = f[i];
		}
	}
	printf("%lld\n", 1ll * m * f[n] - 1ll * sum[n] * sum[n]);
	return 0;
}



#include<bits/stdc++.h>
#define db double
#define ll long long
#define fi first
#define se second
#define pii pair<ll,int>
#define mkp(x,y) (make_pair(x,y))
using namespace std;
const int MAXN = 3e3 + 7;
const db eps = 1e-7;
int n, m;
int a[MAXN];
ll sum[MAXN];
int que[MAXN];
pii f[MAXN];

inline pii calc(int x, int y) {
	return mkp(f[y].fi + 1ll * (sum[x] - sum[y]) * (sum[x] - sum[y]), 1 + f[y].se);
}

inline db k(int x) {
	return 2.0 * sum[x];
}

//y f_x+sum_x^2
inline ll gety(int b) {
	return f[b].fi + 1ll * sum[b] * sum[b];
}
//x sum_x
//k 2*sum_k
//b f_y-sum_y^2
inline db getslope(int x, int y) {
	return (db)(gety(y) - gety(x)) / (db)(sum[y] - sum[x]);//del(y)/del(x);
}

inline pii solve(int x) {
	int h = 1, t = 1;
	for(int i = 1; i <= n; ++i) {
		f[i].fi = 0;
		f[i].se = 0;
	}
	que[h] = 0;
	for(int i = 1; i <= n; ++i) {
		while(h < t && getslope(que[h], que[h + 1])  < k(i)) {
			// printf("%d %d %d %lf %lf\n", i, que[h], que[h + 1], getslope(que[h], que[h + 1]), k(i));
			++h;
		}
		// printf("%d %d %d %lf %lf\n", i, que[h], que[h + 1], getslope(que[h], que[h + 1]), k(i));
		// printf("in->%d %d?%d %d\n", i, que[h], h, t);
		f[i] = calc(i, que[h]);
		f[i].fi += x;
		// printf("dedaole :%d?%d %d?\n", f[i], f[que[h]], sum[i] - sum[que[h]]);
		// if(h < t)
		// 	printf("now tb is :%lf?%lf\n", getslope(que[h], que[h + 1]), k(i));
		while(h < t && getslope(que[t], que[t - 1])  > getslope(que[t], i))
			--t;
		que[++t] = i;
	}
	return f[n];
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		sum[i] = sum[i - 1] + a[i];
		// g[i] = sum[i] * sum[i];
		// printf("%d?\n", sum[i]);
	}
	int l = -1e9, r = 1e9, mid, Ans = 0;
	for(int i = 1; i <= 36; ++i) {
		mid = (l + r) >> 1;
		// printf("%d %d %d\n", l, r, mid);
		solve(mid);
		if(f[n].se > m) {
			l = mid;
		} else {
			Ans = f[n].fi - m * mid;
			r = mid;
		}
	}
	printf("%lld\n", 1ll * m * Ans - 1ll * sum[n]*sum[n]);
	return 0;
}