【洛谷 P3648】 [APIO2014]序列分割 (斜率优化)

题目链接
假设有\(3\)\(a,b,c\)
先切\(ab\)和先切\(bc\)的价值分别为
\(a(b+c)+bc=ab+bc+ac\)
\((a+b)c+ab=ab+bc+ac\)
归纳一下可以发现切的顺序并不影响总价值。
于是设\(f[i][j]\)表示前\(i\)个数切\(j\)次的最大价值,转移方程就很简单了。
然后斜率优化一下就能降时间复杂度降到\(O(nk)\)
\(f[i][j]=f[k][j-1]+sum[k]*(sum[i]-sum[k])\)
\(f[k][j-1]-sum[k]^2=-sum[i]*sum[k]+f[i][j]\)
水分神器斜率优化

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int MAXM = 205; 
#define ll long long
#define re register

int n, m, p;
int q[MAXN], head, tail, sum[MAXN], fa[MAXN][MAXM];
ll f[MAXN][MAXM];

inline double k(int t, int i, int j){
	if(sum[i] == sum[j]) return -2e18;
	return (double)(f[i][t] - f[j][t] - (ll)sum[i] * sum[i] + (ll)sum[j] * sum[j]) / (sum[i] - sum[j]);
}
int main(){
	scanf("%d%d", &n, &m);
	for(re int i = 1; i <= n; ++i){
	   scanf("%d", &p);
	   sum[i] = sum[i - 1] + p;
    }
	for(re int j = 1; j <= m; ++j){
	   head = tail = 0;
	   for(re int i = 1; i <= n; ++i){
	      while(head < tail && k(j - 1, q[head], q[head + 1]) > -sum[i]) ++head;
	      re int l = q[head];
	      f[i][j] = f[l][j - 1] + (ll)sum[l] * (sum[i] - sum[l]);
	      fa[i][j] = l;
	      while(head < tail && k(j - 1, q[tail - 1], q[tail]) <= k(j - 1, q[tail], i)) --tail;
	      q[++tail] = i;
	   }
    }
    printf("%lld\n", f[n][m]);
    int now = fa[n][m];
    while(m--){
    	printf("%d ", now);
    	now = fa[now][m];
    }
    return 0;
}
posted @ 2019-01-28 10:12  Qihoo360  阅读(148)  评论(0编辑  收藏  举报
You're powerful!