题目
给一个数n<=2e5,一个序列a[],
你可以给出一个整数b,使得最小
输出sum
思路来源
https://www.cnblogs.com/Rye-Catcher/p/9255304.html
https://blog.youkuaiyun.com/haipai1998/article/details/80891065
题解
首先对读入的ai,处理成ai-i,
然后考虑枚举的b,
如果有k个值比b小,则有(n-k)个值比b大
b每增1,k个值由于为负,总绝对值+k,n-k个值为正,总绝对值-(n-k)
譬如n=10,k=1时,有一个值会增加,有九个值会减少,
b可以再增加以使值更小,就直接令k为新的a序列的中位数即可
考虑到b小于中位数时答案一直在减小,而大于中位数后一直在增大
虽然中间可能因为有两个中位数导致一段区间是水平的,
但仍然保持先降中平后增的趋势,因此可以三分最小值
代码1(中位数)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int n,a[maxn],b;
ll ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i]-=i;
}
sort(a+1,a+n+1);
b=a[(n+1)/2];
for(int i=1;i<=n;++i)
ans+=abs(a[i]-b);
printf("%lld\n",ans);
return 0;
}
代码2(三分)
三分最小值时,哪端大,
说明哪端以外的值都不是最小值,
把该断对应的端界缩进来即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int n,a[maxn],b;
int l,r;
ll sum(ll mid)
{
ll res=0;
for(int i=1;i<=n;++i)
res+=abs(a[i]-mid);
return res;
}
ll ans;
int main()
{
l=1e9;r=-1e9;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i]-=i;
l=min(l,a[i]);
r=max(r,a[i]);
}
sort(a+1,a+n+1);
while(r-l>1)
{
ll m=(l+r)>>1,mm=(m+r)>>1;
if(sum(m)>sum(mm))l=m;
else r=mm;
//等于的话说明[m,mm]内答案均可
//但此时为了三分方便,还是把区间缩到一
}
ans=min(sum(l),sum(r));
printf("%lld\n",ans);
return 0;
}