链接
鉴于 \(luogu\) 经常似,这里把 \(Markdown\) 粘过来了
题目
[NOIP2021] 方差
题目描述
给定长度为 \(n\) 的非严格递增正整数数列 \(1 \le a_1 \le a_2 \le \cdots \le a_n\)。每次可以进行的操作是:任意选择一个正整数 \(1 < i < n\),将 \(a_i\) 变为 \(a_{i - 1} + a_{i + 1} - a_i\)。求在若干次操作之后,该数列的方差最小值是多少。请输出最小值乘以 \(n^2\) 的结果。
其中方差的定义为:数列中每个数与平均值的差的平方的平均值。更形式化地说,方差的定义为 \(D = \frac{1}{n} \sum_{i = 1}^{n} {(a_i - \bar a)}^2\),其中 \(\bar a = \frac{1}{n} \sum_{i = 1}^{n} a_i\)。
输入格式
输入的第一行包含一个正整数 \(n\),保证 \(n \le {10}^4\)。
输入的第二行有 \(n\) 个正整数,其中第 \(i\) 个数字表示 \(a_i\) 的值。数据保证 \(1 \le a_1 \le a_2 \le \cdots \le a_n\)。
输出格式
输出仅一行,包含一个非负整数,表示你所求的方差的最小值的 \(n^2\) 倍。
样例 #1
样例输入 #1
4
1 2 4 6
样例输出 #1
52
提示
【样例解释 #1】
对于 \((a_1, a_2, a_3, a_4) = (1, 2, 4, 6)\),第一次操作得到的数列有 \((1, 3, 4, 6)\),第二次操作得到的新的数列有 \((1, 3, 5, 6)\)。之后无法得到新的数列。
对于 \((a_1, a_2, a_3, a_4) = (1, 2, 4, 6)\),平均值为 \(\frac{13}{4}\),方差为 \(\frac{1}{4}({(1 - \frac{13}{4})}^2 + {(2 - \frac{13}{4})}^2 + {(4 - \frac{13}{4})}^2 + {(6 - \frac{13}{4})}^2) = \frac{59}{16}\)。
对于 \((a_1, a_2, a_3, a_4) = (1, 3, 4, 6)\),平均值为 \(\frac{7}{2}\),方差为 \(\frac{1}{4} ({(1 - \frac{7}{2})}^2 + {(3 - \frac{7}{2})}^2 + {(4 - \frac{7}{2})}^2 + {(6 - \frac{7}{2})}^2) = \frac{13}{4}\)。
对于 \((a_1, a_2, a_3, a_4) = (1, 3, 5, 6)\),平均值为 \(\frac{15}{4}\),方差为 \(\frac{1}{4} ({(1 - \frac{15}{4})}^2 + {(3 - \frac{15}{4})}^2 + {(5 - \frac{15}{4})}^2 + {(6 - \frac{15}{4})}^2) = \frac{59}{16}\)。
【数据范围】
测试点编号 | \(n \le\) | \(a_i \le\) |
---|---|---|
\(1 \sim 3\) | \(4\) | \(10\) |
\(4 \sim 5\) | \(10\) | \(40\) |
\(6 \sim 8\) | \(15\) | \(20\) |
\(9 \sim 12\) | \(20\) | \(300\) |
\(13 \sim 15\) | \(50\) | \(70\) |
\(16 \sim 18\) | \(100\) | \(40\) |
\(19 \sim 22\) | \(400\) | \(600\) |
\(23 \sim 25\) | \({10}^4\) | \(50\) |
对于所有的数据,保证 \(1 \le n \le {10}^4\),\(1 \le a_i \le 600\)。
历时一天半,共八届奥赛课加两节数学自习的努力,终于给他 \(A\) 了,下面简单回顾一下我这悲苦的历程
题目一开始是没什么思路的,但入手点一定得在操作上找,然后就设一个 \(x_1,x_2,x_3,x_4\) 开始自己胡乱手模
然后我就惊奇的发现,只模这个数是没什么作用的(因为我半个小时后什么也没得出来),然后就不看这个数了,开始
看俩数操作后的关系,就做了个差,然后。。就发现了一个惊奇的性质:操作一个数 \(x_i\) 相当于把 \(x\) 的查分数组 \(d\) 中的
\(d_{i-1}\) 和 \(d_{i}\) 互换位置
那现在问题就转化成了找出一个 \(d\) 的排列使方差最小,然后就开始推式子了。。。
记 \(a_i=a_1+\sum_{j=1}^{i-1} \limits x_j\) , \(\bar a=a_1+\frac{1}{n}\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j\)
\[n^2D = n\sum_{i = 1}^{n} {(a_i - \bar a)}^2 \]\[= n\sum_{i = 1}^{n} {(a_1+\sum_{j=1}^{i-1} \limits x_j -a_1-\frac{1}{n}\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j)}^2 \]\[= n\sum_{i = 1}^{n} {\left[(\sum_{j=1}^{i-1} \limits x_j)^2-\frac{2}{n}\sum_{j=1}^{i-1} \limits x_j\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j+\frac{1}{n^2}(\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j)^2\right]} \]\[=\sum_{i = 1}^{n} {\left[n(\sum_{j=1}^{i-1} \limits x_j)^2-2\sum_{j=1}^{i-1} \limits x_j\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j+\frac{1}{n}(\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j)^2\right]} \]\[=n\sum_{i = 1}^{n}(\sum_{j=1}^{i-1} \limits x_j)^2-2\sum_{i = 1}^{n}\sum_{j=1}^{i-1} \limits x_j\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j+\frac{1}{n}\sum_{i = 1}^{n}(\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j)^2 \]\[=n\sum_{i = 1}^{n}(\sum_{j=1}^{i-1} \limits x_j)^2-(\sum_{i=1}^{n}\limits\sum_{j=1}^{i-1}\limits x_j)^2 \]\[=n\sum_{i = 1}^{n-1}(\sum_{j=1}^{i} \limits x_j)^2-(\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j)^2 \]这就是我们答案的初始式子了,但我们不知道 \(x\) 序列的排列方式,所以。。。继续推
\[n\sum_{i = 1}^{n-1}(\sum_{j=1}^{i} \limits x_j)^2-(\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j)^2 \]\[=n\sum_{i = 1}^{n-1}\sum_{j=1}^{i} \limits\sum_{k=1}^{i} \limits x_j*x_k-(\sum_{i=1}^{n-1}\limits x_i*(n-i))^2 \]\[=n\sum_{i = 1}^{n-1}\sum_{j=1}^{n-1} \limits x_i*x_j*(n-\max(i,j))-\sum_{i=1}^{n-1}\limits \sum_{j=1}^{n-1}\limits x_i*x_j*(n-i)*(n-j) \]\[=\sum_{i = 1}^{n-1}\sum_{j=1}^{n-1} \limits x_i*x_j*\left[n*(n-\max(i,j))-(n-i)*(n-j)\right] \]\[=\sum_{i = 1}^{n-1}\sum_{j=1}^{n-1} \limits x_i*x_j*\left[n^2-n*\max(i,j)-n^2+(i+j)*n-i*j \right] \]\[=\sum_{i = 1}^{n-1}\sum_{j=1}^{n-1} \limits x_i*x_j*\left[(i+j)*n-n*\max(i,j)-i*j \right] \]\[=\sum_{i = 1}^{n-1}\sum_{j=1}^{n-1} \limits x_i*x_j*\left[n*\min(i,j)-i*j \right] \]我们发现这个 \(min\) 不太好处理,但是我们容易推出 \(\sum_{i = 1}^{n-1}\limits \sum_{j=1}^{n-1} \limits x_i*x_j=2*\sum_{i = 1}^{n-1}\limits \sum_{j=1}^{i-1} \limits x_i*x_j+\sum_{i=1}^{n-1}\limits {x_i}^2\)
所以原式可化为
\[=\sum_{i=1}^{n-1}{x_i}^2*(n-i)*i+2*\sum_{i = 1}^{n-1}\sum_{j=1}^{i-1} \limits x_i*x_j*(n-i)*j \]观察左边的式子发现,当中间部分的 \(x\) 较小时整体较小,这时我们看右边式子,列一下 \(n=7\) 时的矩阵
( i ,j ) | i=2 | i=3 | i=4 | i=5 | i=6 |
---|---|---|---|---|---|
\(j=5\) | \((6,5)\) | ||||
\(j=4\) | \((5,4)\) | \((6,4)\) | |||
\(j=3\) | \((4,3)\) | \((5,3)\) | \((6,3)\) | ||
\(j=2\) | \((3,2)\) | \((4,2)\) | \((5,2)\) | \((6,2)\) | |
\(j=1\) | \((2,1)\) | \((3,1)\) | \((4,1)\) | \((5,1)\) | \((6,1)\) |
我们同时列一下其对应的系数
( i ,j ) | i=2 | i=3 | i=4 | i=5 | i=6 |
---|---|---|---|---|---|
\(j=5\) | \(5\) | ||||
\(j=4\) | \(8\) | \(4\) | |||
\(j=3\) | \(9\) | \(6\) | \(3\) | ||
\(j=2\) | \(8\) | \(6\) | \(4\) | \(2\) | |
\(j=1\) | \(5\) | \(4\) | \(3\) | \(2\) | \(1\) |
我们惊奇的发现,对于右面的式子,也是当中间 \(x\) 的值小时,整体值较小
所以我们就得到了:当 \(x\) 承单谷排列时,使答案最小
但我们又发现一个问题,我们只知道单谷排列,但并不知道“谷”在哪,所以我们就应该 \(DP\) 了
考虑我们初始的式子\(n\sum_{i = 1}^{n-1}\limits (\sum_{j=1}^{i} \limits x_j)^2-(\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j)^2\)
我们想让整体最小,就是想让\(\sum_{i = 1}^{n-1}\limits (\sum_{j=1}^{i} \limits x_j)^2\)最小时,且\((\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j)^2\)最大
但两个变量明显不好求,所以我们钦定\(\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j\)不变,去找每一个不同的和对应的最小的\(\sum_{i = 1}^{n-1}\limits (\sum_{j=1}^{i} \limits x_j)^2\)
这里我们的转移方程也出来了:\(f_{i,j}\) 表示考虑前 \(i\) 个差分数组,\(\sum_{i=1}^{n-1}\limits\sum_{j=1}^{i}\limits x_j=j\) 时,最小的\(\sum_{i = 1}^{n-1}\limits (\sum_{j=1}^{i} \limits x_j)^2\)
这里应该提前从小到大排个序
考虑新进来一个 \(x_{i+1}\) 对答案的影响,设 \(sum_i\) 为前 \(i\) 个差分序列的和
- 1:如果放在序列右面:
- 2:如果放在序列左面:相当于之前填的序列整体右移
贡献为$$\sum_{j=1}^{i} (x_j+x_{i+1})^2- \sum_{j=1}^{i} x_j^2$$
\[=\sum_{j=1}^{i} {x_j}^2+\sum_{j=1}^{i} 2*x_j*x_{i+1}+\sum_{j=1}^{i} x_{i+1}^2- \sum_{j=1}^{i} x_j^2 \]\[=\sum_{j=1}^{i} 2*x_j*x_{i+1}+\sum_{j=1}^{i} x_{i+1}^2 \]\[=2*j*x_{i+1}+i*x_{i+1}^2 \]最后加他本身的 \(x_{i+1}^2\)
\[\huge f_{i+1,j+x_{i+1}*(i+1)}=\min f_{i,j}+2*j*x_{i+1}+(i+1)*x_{i+1}^2 \]点击查看代码
#include<bits/stdc++.h>
const int maxn=1e4+10;
using namespace std;
int n,a[maxn],x[maxn],f[2][500001],sum[maxn],top,s,ans;
int main()
{
// freopen("set1.in","r",stdin);
// freopen("T.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
x[i-1]=i>1?a[i]-a[i-1]:0;
// cout<<x[i-1]<<endl;
}
sort(x+1,x+n);
for(int i=1;i<n;i++) sum[i]=sum[i-1]+x[i];
memset(f,0x7f,sizeof f);
f[1][x[1]]=x[1]*x[1];
top=x[1];
int temp=0;
for(int i=1;i<n-1;i++)
{
temp^=1;
top+=x[i+1]*(i+1);
for(int j=0;j<=top;j++)
{
s=j+sum[i+1];
// cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
// if(s>top) continue;
f[temp^1][s]=min(1ll*f[temp^1][s],f[temp][j]+1ll*sum[i+1]*sum[i+1]);
}
for(int j=0;j<=top;j++)
{
s=j+x[i+1]*(i+1);
// if(s>top) continue;
f[temp^1][s]=min(1ll*f[temp^1][s],f[temp][j]+1ll*x[i+1]*x[i+1]*(i+1)+2ll*j*x[i+1]);
}
memset(f[temp],0x7f,sizeof f[temp]);
}
ans=0x7f7f7f7f;
for(int i=0;i<=top;i++)
// cout<<f[(n-1)&1][i]<<endl;
ans=min(1ll*ans,1ll*n*f[(n-1)&1][i]-1ll*i*i);
cout<<ans<<'\n';
return 0;
}