【HDU】2829 Lawrence

http://acm.hdu.edu.cn/showproblem.php?pid=2829

题意:将长度为n的序列分成p+1块,使得$\sum_{每块}\sum_{i<j} a[i]a[j]$最小(n<=1000,1<=a[i]<=100)

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <iostream>

using namespace std;

const int N=10005;

typedef long long ll;

ll d[2][N], s1[N], s2[N];

int n, p, x[N], s[2][N];

inline ll sqr(ll a) { return a*a; }

inline ll w(int l, int r) { return (sqr(s1[r]-s1[l-1])-(s2[r]-s2[l-1]))>>1; }

int main() {

	while(scanf("%d%d", &n, &p), !(n==0&&p==0)) {

		++p;

		for(int i=1; i<=n; ++i) scanf("%d", &x[i]), s1[i]=s1[i-1]+x[i], s2[i]=s2[i-1]+sqr(x[i]);

		int h=0, t=1;

		for(int i=2; i<=n; ++i) d[h][i]=w(1, i), s[h][i]=1;

		for(int i=2; i<=p; ++i) {

			s[t][n+1]=n;

			for(int j=n; j>=1; --j) {

				int l=s[h][j], r=s[t][j+1], &pos=s[t][j]; ll &now=d[t][j];

				now=~0ull>>1;

				for(int k=l; k<=r; ++k) {

					ll t=d[h][k-1]+w(k, j);

					if(now>=t) now=t, pos=k;

				}

			}

			swap(t, h);

		}

		printf("%lld\n", d[h][n]);

	}

	return 0;

}

  

设$d(i, j)$表示分$i$份前$j$个的最小代价,容易得到

$$d(i, j) = min \{ d(i-1, k-1) + w(k, j) \}, i<j $$

其中

$$w(i, j) = \frac{sum1(i, j)^2-sum2(i, j)}{-2}$$

其中$sum1(i, j)=\sum_{i}^{j} a[i], sum2(i, j)=\sum_{i}^{j} a[i]^2$

(可以由$(\sum_{i} a[i] )^2 = \sum_{i} a[i]^2 - 2 \sum_{i<j} a[i]a[j]$得到= =,然后$O(n)$求出来,虽然你们都是用$O(n^2)$预处理的= =(我有常数强迫症= =))

证明挺好证的(这里只证明$w$的四边形不等式,剩下证明与前面两题相同= =我就不证明了= =

证明四边形不等式即证明当$j$固定时,$w(i, j+1)-w(i, j)$是关于$i$的递减函数

$$
\begin{align}
& w(i, j+1)-w(i, j) \notag \\
= & \frac{sum1(i, j+1)^2-sum2(i, j+1)}{-2} - \frac{sum1(i, j)^2-sum2(i, j)}{-2} \\
= & \frac{sum1(i, j+1)^2 - sum1(i, j)^2 + sum2(i, j) - sum2(i, j+1)}{-2} \\
= & \frac{a[j+1]^2 - 2a[j+1]\sum_{k=i}^{j} a[k] - a[j+1]^2}{-2} \\
= & a[j+1]\sum_{k=i}^{j} a[k]
\end{align}
$$

当j不变时i递增,显然式子减小,得证。

你可能感兴趣的:(HDU)