2021icpc济南 Arithmetic Sequence(数学+三分)

题目链接:题目详情 (pintia.cn)

题意:给你n个数,你可以对这n个数进行操作,每次操作可以对一个数进行+1或者-1,问你使得使得这n个数成为一个等差数列的最少操作次数。

分析:这道题目可以三分枚举公差来做,当公差确定后,这就变成了一个更加具体的小问题,也就是给你n个数,问使得这n个数成为一个公差为d的等差数列的最少操作次数,我们不妨假设这个公差为d的等差数列 c [ ] 的首项为x,则c[i]=x+(i-1)*d,则对于第i项而言我们就需要操作 | a[ i ] - c[ i ] |次,也就是 | a[ i ] - ( x+(i-1)*d ) |次,变形一下就可以得到 | x - ( a[ i ]-(i-1)*d ) |,我们令p [ i ]=a[ i ]-(i-1)*d,则等价于求 | x - p [ i ] |的和,i属于1~n,这不就是一个货仓选址问题吗,我们应该让x等于p数组中的中间值,这样我们就可以求出公差为d的数列中所需操作次数最少的数列的首项,那么我们也就不难求出从原数组转化为该公差为d的数组的操作次数为多少,我们把子问题解决了,剩下的就是对公差d的三分了。

需要注意的一点是这道题目数据范围比较大,所以很多地方需要开__int128

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll a[N],n;
__int128 c[N];
__int128 f(ll d)
{
	for(int i=1;i<=n;i++)
		c[i]=a[i]-(i-1)*d;
	__int128 ans=0;
	nth_element(c+1,c+(n+1)/2,c+n+1);
	__int128 k=c[(n+1)/2];
	for(int i=1;i<=n;i++)
	{
		__int128 temp=a[i]-k-(i-1)*d;
		if(temp>0) ans+=temp;
		else ans-=temp;
	}
	return ans;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	ll l=-2e13,r=2e13,lmid,rmid;
	while(l<r)
	{
		lmid=l+(r-l)/3;
		rmid=r-(r-l)/3;
		if(f(lmid)<f(rmid)) r=rmid-1;
		else l=lmid+1;
	}
	cout<<(ll)f(l);
	return 0;
}

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值