ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOIP2021] 方差

2022-04-16 09:34:27  阅读:166  来源: 互联网

标签:pre 方差 rep times NOIP2021 dp define


前言

\(\texttt{p}\color{red}{\texttt{igstd}}\) 场切的题,我到现在才来补。

导致我爆炸的题。

大意

给你一个非降的序列 \(a\),每次可以将 \(a_i\) 变成 \(a_{i-1}+a_{i+1}-a_i\),求若干次操作之后方差最小是多少,输出最小值乘 \(n^2\)。

Sol

首先是大家都知道的,这一次操作相当于交换差分数组中的两个元素。说明我们可以对差分数组重排,虽然但是我并不知道排成什么样子是最小的。

\(\texttt{p}\color{red}{\texttt{igstd}}\) 告诉我们,要手模样例。然后你发现样例是一个长度小于等于 \(3\) 的勾八。然后你写了个暴力跑了个 \(n=50\) 的手造高质量数据,发现在差分数组先变小后变大的时候是非常优秀的(也就是原数组在这种情况下非常均匀)!但是你发现你根本不会证这个东西,于是你考场上就 30pts 滚粗了。。。

题解区一大片的显然容易证明搞得我很无所适从啊~有个大佬看起来是证明了这个东西。

我不会证,但是 OI 不就是只用不证的么(((。现在我们假设已经证明了这个结论,那么我们容易想到一个 \(2^n\) 的暴力做法,就是从小到大依次枚举方差,看是放在最低点的左边还是右边。然后这东西看着就很可以大炮。

令 \(dp[i][j]\) 表示考虑前 \(i\) 个方差后,当前原数列和为 \(j\) 的最小的原数列平方和(因为我们知道方差表达式为平方的平均数减去平均数的平方)。然后考虑大力转移。

如果当前这个放在最左边,那么最后的和会加上 \(d_i\times i\),平方和呢?考虑在前面放一个差分实际上是在整个数列上加上这个数,那就是考虑 \(a^2\to (a+d)^2=a^2+2ad+d^2\),所以答案应该加上和乘上这个差分的两倍再加上这个差分的平方。所以转移就是:

\[dp[i][j]=dp[i-1][j-d_i\times i]+(j-d_i\times i)\times d_i\times 2+i\times d_i^2 \]

同样的,如果是放在后面可以轻易地进行转移,此时,上一个状态应该要减去 \(pre_i\),即 \(d_i\) 的前缀和。对答案的影响也就是多了一个平方,比上面那个要好推。

\[dp[i][j]=dp[i-1][j-pre_i]+pre_i^2 \]

然后你考虑这么做的复杂度是多少,你发现是 \(O(n^2\times a)\) 的,会寄,然后你想了想,如果当前的方差是 \(0\),对大炮状态不会产生任何影响,所以不转移也罢。这就变成了 \(O(n\times a^2)\) 了,就过了。

空间好像需要滚一下。

Code

// Problem: P7962 [NOIP2021] 方差
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7962
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Time: 2022-04-14 22:04:18

#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
using namespace std;
const int MAXN=1e4+10;
int dp[2][MAXN*600],a[MAXN],d[MAXN],pre[MAXN];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n,sum=0;cin>>n;
	rep(i,1,n) cin>>a[i];
	rep(i,1,n-1) d[i]=a[i+1]-a[i];
	sort(d+1,d+n);
	rep(i,1,n-1) sum+=d[i]*i,pre[i]=pre[i-1]+d[i];
	int cnt=0;
	while(!d[cnt+1]) cnt++;
	rep(j,1,sum) dp[cnt&1][j]=INF;
	rep(i,cnt+1,n-1){
		rep(j,0,sum){
			dp[i&1][j]=INF;
			if(j-d[i]*i>=0)
			dp[i&1][j]=min(dp[i&1][j],dp[(i-1)&1][j-d[i]*i]+(j-d[i]*i)*d[i]*2+i*d[i]*d[i]);
			if(j-pre[i]>=0)
			dp[i&1][j]=min(dp[i&1][j],dp[(i-1)&1][j-pre[i]]+pre[i]*pre[i]);
			// if(dp[i&1][j]<INF) pt(i),pt(j),pts(dp[i&1][j]);
		}
	}
	int ans=INF;
	rep(j,0,sum)
		if(dp[(n-1)&1][j]<INF)
		ans=min(ans,n*dp[(n-1)&1][j]-j*j);
	cout<<ans<<'\n';
	return 0;
}

标签:pre,方差,rep,times,NOIP2021,dp,define
来源: https://www.cnblogs.com/ZCETHAN/p/16151853.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有