D、Arithmetic Sequence(三分算法+货仓选址模型)

Alice received a sequence of n integers as her birthday gift. As she likes arithmetic sequences, she wants to turn her gift into an arithmetic one. In an arithmetic sequence, the difference between one term and the next is a constant.
She can use her magical power and cast spells on a sequence. And she can cast two types of spells. The first type is the ‘‘increment spell’’: When she uses this spell, she can choose a number in this sequence and add this number by one. The other type, as you have guessed, is the ‘‘decrement spell’’: She can choose a number in this sequence and subtract this number by one. Casting either type of spell costs her 1 Mana (the unit of magical power).
Now she wonders about the minimum Manas she would use to make her gift arithmetic. Alice is not proficient in math skills, so she asks for your help.

Input
The first line consists of an integer n (1≤n≤2×105) — the length of the sequence.
The second line consists of n integers ai(0≤ |ai| ≤1013 and1≤ i ≤ n) separated by space — the initial sequence.
Output
Output only one integer — the minimum Manas she would use to make the sequence arithmetic.
Sample Input
5
2 4 7 9 9
Sample Output
3

题意:

Alice有一个n个数字的序列,她可以选择一次操作:对任意一个数字进行加一或者减一操作,问最少操作几次可以让这个序列成为等差序列。
题解:
找到一个等差序列,其对应位置与原数组作差,每个位置的差值取绝对值加和,要使和最小。显然操作次数随公差的变化是一个凸函数,因此做法就是三分公差,然后对每次枚举出的左边界和右边界比较,直到找出两边相等时即找出最小的公差,然后输出。
计算操作次数时,已知公差,所以再需要知道任意一项的值就可以求出目标数列,
设公差为d,首项为x,原序列a[n]
则操作次数为| x-a[1] |+| x+d-a[1] |+...+| x+(n-1)d-a[n] |

货舱选址模型讲解:
https://www.cnblogs.com/LLTYYC/p/9537677.html

货舱选址模型中要使得次数最少,x 的值就等于a[i]-(i-1)*d 序列的中位数。
累加绝对值的过程中会爆掉long long,因为会有n次相加1e18数量级的数据,需要用 __int128。
找中位数的时候不能用 sort() 函数,会TLE。
在这里插入图片描述

AC代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>

using namespace std;
typedef long long ll;
const int MAXN=2e5+10;
ll n;
__int128 sub[MAXN];
ll num[MAXN];

__int128 find(__int128 d)
{
    __int128 ans=0;
    for(ll i=1;i<=n;i++){
		sub[i]=num[i]-(i-1)*d;
    }
    nth_element(sub+1,sub+(n+1)/2,sub+n+1);//求中位数 
    __int128 mid=sub[(n+1)/2];//取中位数
    for (ll i=1;i<=n;i++){
        __int128 tmp=mid+(i-1)*d-num[i];
        tmp>0 ? ans+=tmp : ans-=tmp;//累加操作次数绝对值 
    }
    return ans;
}
int main()
{
	cin>>n;
	for(ll i=1;i<=n;i++)
		cin>>num[i];
	//三分算法 
	__int128 l=-1e13,r=1e13;
	while(l<r){
    	__int128 midl=l+(r-l)/3;
        __int128 midr=r-(r-l)/3;
        if(find(midl)<find(midr))
			r=midr-1;
        else l=midl+1;
    }
    ll ans=find(l);
    cout<<ans<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值