2022年牛客多校第2场 J . Link with Arithmetic Progression (三分+枚举)

题意
给一个序列,对于其中的元素可以改变为任意值,花费为差值的平方,问将其改编为等差序列的最小花费
思路

假设之前的序列为a1,a2,a3……
修改后的序列为b1,b2,b3……
首项为b1,公差为d
差值ci=ai-bi
bi=b1+(i-1)d
答案就是c1
c1+c2c2+……=
(a1-(b1+(i-1)d))^2+ (a2-(b1+(i-1)d))^2+……
然后化简出来发现把公差d当作未知数得到二元一次函数
三分d来缩小范围 check的时候计算代价
知道d以后
ci=ai-bi=ai-(b1+(i-1)d)
ci+b1都是未知的,放到一侧
ci+b1=ai-(i-1)d=di
c1
c1+c2
c2+c3
c3+……
=(d1-b1)^2+ (d2-b1)^ 2+(d3-b1)^2+……
=nb1
b1-2
(d1+d2+d3+……)b1+(d1 * d1+d2 * d2+d3 * d3+……)
b1是未知数
相当于ax^2+by+c求极值,当x=-b/2a时取得极值
b1=(-2
(d1+d2+d3+……))/(-2n)=(d1+d2+d3+……)/n
然后在计算ci就行了
ci=di-b1

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=1e5+10;

long double a[maxn];
long double b[maxn];
long double eps=1e-10;
int n;

long double check(long double d){
	long double res=0,sum=0;
	for(int i=1;i<=n;i++){
		b[i]=(a[i]-(i-1)*d);
		sum=sum+b[i];
	}
	sum/=n;
	for(int i=1;i<=n;i++){
		res=res+(b[i]-sum)*(b[i]-sum);
	}
	return res;
}

int main() {
	int _;scanf("%d",&_);
	while(_--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%Lf",&a[i]);	
		long double l=-1e10,r=1e10;
		while(r-l>eps){
			long double mid1=l+(r-l)/3.0,mid2=r-(r-l)/3.0;
			if(check(mid1)>check(mid2)) l=mid1;
			else r=mid2;
			//cout<<_<<" "<<l<<" "<<r<<endl;
		}
		printf("%.10Lf\n",check(l));
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值