[NOIP2021] 方差

洛谷题面

前置知识:CF1110E。

可以看看写的 \(\textbf{题解}\)

题目大意

给定长度为 \(n\) 的非严格递增正整数数列 \(1 \le a_1 \le a_2 \le \cdots \le a_n\)。每次可以进行的操作是:任意选择一个正整数 \(1 \lt i \lt 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\)

题目分析

前置知识中提到:

\(c\) 为差分数组,且 \(c_i=a_{i+1}-a_i(1\le i\lt n)\)

则一次操作 \(a_i=a_{i-1}+a_{i+1}-a_i\) 等价于交换 \(c_i\)\(c_{i+1}\),那么问题转换为:

给出长度为 \(n\) 的序列,重组原序列的差分数组,将得到的差分数组再求出 \(a\) 数组。求 \(n^2\times\min\{\dfrac{1}{n}\sum\limits_{i=1}^{n}(a_i-\bar a)^2\}\)

显然可以 \(\operatorname{O}(N!)\) 枚举。


有一个重要的性质:答案差分数组一定先递减再递增。

于是可以随机化乱搞,\(84\) 分到手。


理一理式子:

\[n^2\times \dfrac{1}{n}\sum\limits_{i=1}^{n}(a_i-\bar a)^2 \]

\[=n\times [(a_1-\bar a)^2+(a_2-\bar a)^2+\cdots+(a_n-\bar a)^2] \]

\[=n\times (a_1^2+a_2^2+\cdots+a_n^2+n\times {\bar a}^2-(2a_1\bar a+2a_2\bar a+\cdots+2a_n\bar a)) \]

\[=n\times[(a_1^2+a_2^2+\cdots+a_n^2)+n\times {\bar a}^2-2\bar a\times(a_1+a_2+\cdots+a_n)] \]

发现 \((a_1+a_2+\cdots+a_n)\) 就是 \(n\times\bar a\),于是:

\[=n\times(\sum\limits_{i=1}^n a_i^2-n\times \bar a^2) \]

\[=n\times\sum\limits_{i=1}^n a_i^2-n^2\times\bar a^2 \]

由于 \(n^2\times \bar a^2\) 等于 \((n\times \bar a)^2\),即 \((\sum\limits_{i=1}^n a_i)^2\),故有:

\[=n\times\sum\limits_{i=1}^n a_i^2-(\sum\limits_{i=1}^n a_i)^2 \]


\(m=n-1\)\(dp[i][j]\) 表示当前考虑了差分数组的前 \(i\) 个时的 \(\sum\limits_{i=1}^n a_i\)

这样转移会很方便,因为我们要想让 \(a\) 数组满足先减再增的性质,故只需要从小到大排序,然后依次考虑放左边还是放右边就好了。

\(c_i\) 放在右边时:

\(now=\sum\limits_{j=1}^i c_j\),则 \(a_i=now\)(差分的性质)。

方程为 \(dp[i][j]\gets dp[i-1][j-now]+now^2\)

\(c_i\) 放在左边时:

\(a\) 数组中这 \(i\) 个数都会加上 \(c_i\),总共加了 \(i\times c_i\)。此时平方和的变化量

\[\sum\limits_{j=1}^i (a_j+c_i)^2-\sum\limits_{j=1}^i a_j^2 \]

\[=\sum\limits_{j=1}^i c_i^2+\sum\limits_{j=1}^i(2\times a_j\times c_i) \]

\[=2\times c_i\times \sum\limits_{j=1}^i a_j+\sum\limits_{j=1}^i c_i^2 \]

设此时(转移之前)\(dp\) 数组的第二个状态为 \(p\)

\[=2\times c_i\times p+i\times c_i^2 \]

方程为 \(dp[i][j]\gets dp[i-1][j-i\times c_i]+2\times c_i\times (j-i\times c_i)+i\times c_i^2\)

代码

//2021/12/4

//2021/12/6

//2021/12/7

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

#include <cstdio>

#include <climits>//need "INT_MAX","INT_MIN"

#include <algorithm>

#include <cstring>

#define int long long

#define enter() putchar(10)

#define debug(c,que) cerr<<#c<<" = "<<c<<que

#define cek(c) puts(c)

#define blow(arr,st,ed,w) for(register int i=(st);i<=(ed);i++)cout<<arr[i]<<w;

#define speed_up() cin.tie(0),cout.tie(0)

#define endl "\n"

#define Input_Int(n,a) for(register int i=1;i<=n;i++)scanf("%d",a+i);

#define Input_Long(n,a) for(register long long i=1;i<=n;i++)scanf("%lld",a+i);

#define mst(a,k) memset(a,k,sizeof(a))

namespace Newstd
{
	inline int read()
	{
		int x=0,k=1;
		char ch=getchar();
		while(ch<'0' || ch>'9')
		{
			if(ch=='-')
			{
				k=-1;
			}
			ch=getchar();
		}
		while(ch>='0' && ch<='9')
		{
			x=(x<<1)+(x<<3)+ch-'0';
			ch=getchar();
		}
		return x*k;
	}
	inline void write(int x)
	{
		if(x<0)
		{
			putchar('-');
			x=-x;
		}
		if(x>9)
		{
			write(x/10);
		}
		putchar(x%10+'0');
	}
}

using namespace Newstd;

using namespace std;

const int INF=0x3f3f3f3f3f3f3f3f;

const int ma=500005;

int a[ma],b[ma];

int dp[2][ma];

int n,sum;

#undef int

int main(void)
{
	#define int long long

	n=read();
	
	for(register int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	
	for(register int i=1;i<n;i++)
	{
		b[i]=a[i+1]-a[i];
	}
	
	sort(b+1,b+n);
	
	for(register int i=1;i<n;i++)
	{
		sum+=b[i]*i;
	}
	
	mst(dp[0],0x3f);
	
	dp[0][0]=0;
		
	int now(0),idx(0);
	
	for(register int i=1;i<n;i++)
	{
		if(b[i]==0)
		{
			continue;
		}
		
		idx^=1ll;
		
		mst(dp[idx],0x3f);
		
		now+=b[i];
		
		for(register int j=0;j<=sum;j++)
		{
			if(j-i*b[i]>=0)
			{
				dp[idx][j]=min(dp[idx][j],dp[idx^1ll][j-i*b[i]]+i*b[i]*b[i]+2*b[i]*(j-i*b[i]));
			}
			
			if(j-now>=0)
			{
				dp[idx][j]=min(dp[idx][j],dp[idx^1ll][j-now]+now*now);
			}
		}
	}
	
	int ans=INF;
	
	for(register int i=0;i<=sum;i++)
	{
		if(dp[idx][i]<INF)
		{
			ans=min(ans,n*dp[idx][i]-i*i);
		}
	}
	
	printf("%lld\n",ans);
	
	return 0;
}
posted @ 2021-12-07 23:06  Coros_Trusds  阅读(224)  评论(0)    收藏  举报