P7962 [NOIP2021] 方差

标签:DP \(B\)

我们考虑简化题目中要求的式子可以得到:

\[n\sum a_i^2 - (\sum a_i)^2 \]

我们考虑这种题显然先差分找规律,可以发现给的操作在差分数组 \(b\) 上就是交换差分数组上相邻的两个值。

然后我们打表发现答案的差分数组是先减小后增大的,而这个结论的正确性可以使用微调法证明。

于是我们可以从小到大依次加入差分数组中的值,每次选择加入到最左/右边

这显然可以 DP 求解。

\(f_{i,j}\) 表示插入差分数组前 \(i\) 小的值,\(\sum a_i = j\) 时,最小的 \(\sum a_i^2\)

转移如下:

\[\begin{cases} f_{i,j+b_i\times i} \to f_{i,j} + 2 \sum b_i \sum a_i + i \times b_i & 插前面\\ f_{i,j+\sum b} \to f_{i,j} + (\sum b)^2 & 插后面 \end{cases} \]

最后滚动数组压缩空间,然后对于第一个不能改变的数的细节处理一下即可。

记得开 long long!

#include<bits/stdc++.h> 
using namespace std;
const int NN = 5e6+8;
typedef long long ll;

ll n;
ll a[NN],b[NN],c[NN];
ll f[2][NN];

inline ll read(){
	register char c = getchar();
	register ll res = 0;
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) res = res * 10 + c - '0', c = getchar();
	return res;
}

int main(){
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	ll mx = 0;
	for(int i = 1; i < n; ++i) b[i] = a[i+1] - a[i];
	sort(b+1,b+n);
	for(int i = 1; i < n; ++i) c[i] = c[i-1] + b[i];
	for(int i = 0; i <= mx; ++i) f[0][i] = 1e13;
	f[0][0] = 0;
	for(ll i = 1; i < n; ++i){
		ll mxn = max(mx+i*b[i],mx+c[i]);
		for(ll j = 0; j <= mxn; ++j) f[i&1][j] = 1e13;
		for(ll j = 0; j <= mx; ++j){
			if(f[(i+1)&1][j] == 1e13) continue;
			f[i&1][j+i*b[i]] = min(f[i&1][j+i*b[i]],f[(i+1)&1][j] + 2ll*b[i]*j + i*b[i]*b[i]);
			f[i&1][j+c[i]] = min(f[i&1][j+c[i]],f[(i+1)&1][j] + c[i]*c[i]);
		}
		mx = mxn;
	}
	ll ans = 1e18;
	for(ll j = 0; j <= mx; ++j){
		ans = min(1ll * ans,1ll * n*f[(n-1)&1][j] - j*j);
	}
	printf("%lld",ans);
}
posted @ 2024-02-05 10:52  ricky_lin  阅读(24)  评论(0)    收藏  举报