链接:https://www.nowcoder.com/acm/contest/113/B
来源:牛客网
AB序列
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给长度为n的序列A,长度为m的序列B。可以给A序列里每个元素加上x且B序列里每个元素减去x (x可以是负数),问的最小值
输入描述:
第一行两个整数分别表示n,m
接下来一行n个整数表示序列A
接下来一行m个整数表示序列B
输出描述:
输出一个整数表示答案
示例1
输入
4 5
-8 2 -4 10
5 -5 -4 -9 10
输出
57
备注:
1<=n,m<=105
序列中的数为绝对值不超过10^9的整数
思路:f(x)代表,f(x)是一个开口向上的抛物线。三分求最小值
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <math.h>
#include <queue>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
typedef long long LL;
const LL mod=1e9+7;
const int M=2e4;
using namespace std;
const int N =1e5+100;
LL a[N],b[N],n,m;
LL abss(LL x)
{
return x>0?x:-x;
}
LL solve(LL x)
{
LL ans=0;
for(int i=0;i<n;i++)
ans+=abss(a[i]+x);
for(int i=0;i<m;i++)
ans+=abss(b[i]-x);
return ans+abss(x);
}
LL Fl(LL x,LL y)
{
if(x+y<0)
return (x+y-1)/2;
return (x+y)/2;
}
LL Fr(LL x,LL y)
{
if(x+y<0)
return (x+y)/2;
return (x+y+1)/2;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(LL i=0;i<n;i++)
scanf("%lld",&a[i]);
for(LL i=0;i<m;i++)
scanf("%lld",&b[i]);
//因为l和r牵扯到了负数,那么就不能是简单的(l+r)/2了
//因为/2的结果是向0偏移的,例如:l=-4,r=-1,(l+r)/2=-2,
//例如:l=1,r=4,(l+r)/2=2,都是向零靠近的
//但是我们想让midl向l偏移,midr向r偏移,所以分(l+r)>0和<0 两种情况
//这个三分相当于板子,应该是所有的三分都可以使用,只需要更改solve()函数
//以上废话都是为了防止三分死循环
LL l=-1e9,r=1e9,midl,midr,ans;
while(l<r)
{
midl=Fl(l,r);
midr=Fr(midl,r);
//求最小值用<=,求最大值用>=
if(solve(midl)<=solve(midr))
{
r = midr-1;
ans=midl;
}
else
{
l=midl+1;
ans=midr;
}
}
printf("%lld\n",solve(ans));
}
/*
附上一组数据
5 10
-10 -8 -10 -9 -6
5 6 8 -9 -10 -8 -7 -6 5 8
x=5 ans=95
*/