LGP7962 [NOIP 2021] 方差 学习笔记
LGP7962 [NOIP 2021] 方差 学习笔记
前言
对于当前(指 \(\texttt{20250903}\))的我来说,大概改制后 \(\texttt{NOIP}\) 的真题里面,过了这个村就没这个店(离人类还比较接近的题)了。
假设这里有一张图片,展示了洛谷题库,通过标签筛选了 \(\texttt{NOIP}\) 在 \(\texttt{2020}\) 改制后的二十四道题,其中大多数题目都已通过。然而,“比赛”、“移球游戏”、“密码锁”、“棋局”均无提交,“喵了个喵”、“微信步数”状态显示为红叉。除此之外,还有“排水系统”和“字符串匹配”因为涉及算法原因没写。
本文参考WeLikeStudying的题解。
题意简述
你有一个长度为 \(n\) 的单调不降的正整数序列 \(A\)。你可以任意次进行这样的操作:选择一个下标 \(1<i<n\),使 \(a_i\gets a_{i-1}+a_{i+1}-a_i\)。问该数列在操作后的方差的最小值是多少。输出它乘以 \(n^2\) 的结果。
\(n\le 10^4\),\(a_i\le 600\)。
做法解析
从哪入手呢?一点点分析下题面。
看到这么多限制就要想结论。
首先关于方差有个经典结论:设 \(\overline{x^2}\) 为 \(a_1^2,a_2^2,\cdots ,a_n^2\) 的平均值,\(\overline{x}\) 为 \(a_1,a_2,\cdots,a_n\) 的平均值,则 \(s^2=\overline{x^2}-\overline{x}^2\)。
然后看看这个不太说人话的操作,仔细观察,实际上就是在交换差分数组 \(b_i=a_{i+1}-a_i\) 的相邻两项。
再想想方差本身是啥:“方差是和中心偏离的程度,用来衡量一批数据的波动大小”。于是我们思考,数据会长什么样子,使得它趋近于某个值。鉴于 \(A\) 单调不降,我们很快发现,当差分数组呈单谷状时 \(A\) 的方差大概最小。换句话说,方差最小时 \(B\) 肯定是单谷的,需要解决的问题在于 \(B\) 数组里的元素要怎么分到谷的两侧。

实际上这可以严谨推导证明,参考洛谷某高赞题解或这篇式子没那么大块的题解,读者自证不难。
现在就是 \(\texttt{DP}\) 的事了。我们要 \(\texttt{DP}\) 什么呢?
你已经知道了 \(s^2=\overline{x^2}-\overline{x}^2\)。而题目要你输出 \(s^2\times n^2\),也就是我们直接求最小化 \(n\times(\sum a_i^2)-(\sum a_i)^2\)。可见我们的答案取决于 \(\sum a_i^2\) 和 \(\sum a_i\)(废话),那么我们把一个写进 \(\texttt{DP}\) 的维度里,另一个作为 \(\texttt{DP}\) 求的东西即可。
于是我们很自然地设 \(f_{i,x}\) 为考虑了差分数组 \(B\) 的前 \(i\) 个元素,\(\sum a_i=x\) 时 \(\sum a_i^2\) 的最小值。
转移就呼之欲出了。我们往谷底左边插元素即有:\(f_{i-1,x}+i\times b_i^2+2x\cdot b_i\to f_{i,x+b_i\times i}\),往谷底右边插元素即有:\(f_{i-1,x}+(\sum_{j=1}^i b_j)^2 \to f_{i,x+\sum_{j=1}^i b_j}\)。相信你可以轻松理解。
然后这东西显然能滚动数组。时间复杂度 \(O(n\times n\times a)\),然后……超时了?
实际上,对于 \(B\) 数组来说,最多有 \(\max a_i\) 个差分值是非 \(0\) 的,原因显然。我们提前把那些 \(0\) 处理一下即可,详情见代码。
时间复杂度 \(O(n\times\min(n,a)\times a)\)。可以通过。
代码实现
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e4+5,MaxV=6e6+5,Inf=0x7fffffff;
int N,mxa,A[MaxN],B[MaxN],bsum[MaxN],zcnt,V,ci,pi;
int dp[2][MaxV],ans=Inf;
int main(){
readi(N);
for(int i=1;i<=N;i++)readi(A[i]),maxxer(mxa,A[i]);
for(int i=1;i<N;i++)B[i]=A[i+1]-A[i];
sort(B+1,B+N);V=mxa*N,ci=0,pi=1;
for(int i=1;i<N;i++)bsum[i]=bsum[i-1]+B[i],zcnt+=(!B[i]);
dp[ci][0]=0;for(int x=1;x<=V;x++)dp[ci][x]=Inf;
for(int i=zcnt+1;i<N;i++){
ci^=1,pi^=1;
for(int x=0;x<=V;x++)dp[ci][x]=Inf;
for(int x=0;x<=V;x++){
if(dp[pi][x]==Inf)continue;
minner(dp[ci][x+B[i]*i],dp[pi][x]+i*B[i]*B[i]+2*x*B[i]);
minner(dp[ci][x+bsum[i]],dp[pi][x]+bsum[i]*bsum[i]);
}
}
for(int i=0;i<=V;i++)if(dp[ci][i]!=Inf)minner(ans,N*dp[ci][i]-i*i);
writil(ans);
return 0;
}
后记
\(\texttt{UPD on 20251014}\):
现在,“移球游戏”、“密码锁”、“棋局”、“喵了个喵”、“微信步数”、“排水系统”均已通过。改制后省级联赛的题目中,只有“比赛”保持着红叉,“字符串匹配”暂无提交。继续加油!
浙公网安备 33010602011771号