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);
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/18007458

浙公网安备 33010602011771号