n¯a=n∑i=1ain2D=nn∑i=1(ai−¯a)2=nn∑i=1a2i−2n¯an∑i=1ai+n2¯a2=nn∑i=1a2i−2(n∑i=1ai)2+(n∑i=1ai)2=nn∑i=1a2i−(n∑i=1ai)2
观察题目中的式子,a′i←ai−1+ai+1−ai,根据 CF1110E 的套路,可以差分,令 di=ai+1−ai(1≤i<n),一次操作 (2≤i<n) 即:
d′i−1=a′i−a′i−1=(ai−1+ai+1−ai)−ai−1=ai+1−ai=did′i=a′i+1−a′i=ai+1−(ai−1+ai+1−ai)=ai−ai−1=di−1
相当于交换 di−1,di(2≤i<n),故 d 可以通过若干次操作,变为任意 d 的排列。
由于 a1 不变,那么 a 和 d 是一一对应的。现在要求一个 d 的排列使得 n2D 最小,继续推式子: n2D=nn∑i=1a2i−(n∑i=1ai)2=nn∑i=1a2i−n∑i=1n∑j=1aiaj=12(nn∑i=1a2i−2n∑i=1n∑j=1aiaj+nn∑j=1a2j)=12(n∑i=1n∑j=1(ai−aj)2)=n−1∑i=1n−1∑j=i(aj+1−ai)2=n−1∑i=1n−1∑j=i(di+di+1+⋯+dj)2 n2D 取最小值时,d 一定是先递减后递增的。
考虑在分界位置(即递减到递增)从小到大往两边加数,由于 n2D=nn∑i=1a2i−(n∑i=1ai)2 维护 dp[k][s] 表示当前已经加入了 k 个数,现在的 a 之和为 s 的最小 n∑i=1a2i。
初始 dp[1][s]=0。
转移时考虑加到左边还是右边:
- 左边:原来是 a1,a2,⋯,ak,现在变为 d,a1+d,a2+d,⋯,ak+d,新增的贡献为
Δ=d2+k∑i=1(d+ai)2−k∑i=1a2i=(k+1)d2+2dk∑i=1ai=(k+1)d2+2ds
- 右边:原来是 a1,a2,⋯,ak,现在变为 a1,a2,⋯,ak,ak+d,新增的贡献为 Δ=(ak+d)2 其实可以发现 ak 是固定的,为之前所有的 d 之和,不需要再记录。
答案为 min。
分析一下时间复杂度,第一维是 O(n) 的,第二维是 O(nV) 的,转移 O(1)。
但是可以发现 d 为 0 的转移可以忽略,第一维是 O(\min(n,V)) 的,总复杂度为 O(nV^2),可以通过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
int n,a[11000],d[11000]; ll dp[510000];
ll sqr(ll x){ return x*x;}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<n;i++) d[i]=a[i+1]-a[i];
sort(d+1,d+n);
for (int i=0;i<=500000;i++) dp[i]=INF;
dp[0]=0; int lim=0,sum=0;
for (int i=1;i<n;i++){
if (!d[i]) continue;
for (int s=lim;s>=0;s--){
if (dp[s]==INF) continue;
dp[s+sum+d[i]]=min(dp[s+sum+d[i]],dp[s]+sqr(sum+d[i]));
dp[s+i*d[i]]=min(dp[s+i*d[i]],dp[s]+2*s*d[i]+i*sqr(d[i]));
dp[s]=INF;
}
lim+=i*d[i]; sum+=d[i];
}
ll ans=INF;
for (int i=0;i<=lim;i++)
if (dp[i]!=INF) ans=min(ans,n*dp[i]-1ll*i*i);
printf("%lld\n",ans);
return 0;
}