牛客挑战赛43 C-最优公式 二分,切比雪夫距离转曼哈顿距离
最优公式
题意
有一个长为 \(n\) 的数组 \(A1,A2,…,An\)。
你需要找到两个实数 \(a\), \(b\),使得 \(\sum_{i=1}^n \sum_{j=1}^n \max (|A_i - a|, |A_j - b|)\) 尽可能小。
求出这个最小值。
容易发现答案乘上 2 一定是整数,求出答案乘上 2 模 \(10^9+7\) 的值。
分析
将一个点\((x,y)\)的坐标变为\((x+y,x-y)\)后,原坐标系中的曼哈顿距离 = 新坐标系中的切比雪夫距离
将一个点\((x,y)\)的坐标变为\((\frac{x+y}{2},\frac{x-y}{2})\)后,原坐标系中的切比雪夫距离 = 新坐标系中的曼哈顿距离
将题意转化为\(n^2\)个点\((A_i,A_j)\),找到一个点\((a,b)\),使得这\(n^2\)个点和\((a,b)\)的切比雪夫距离之和最小,将\((A_i,A_j)\)转为\((A_i+A_j,A_i-A_j)\),因为答案要乘二,所以这样转化之后,求切比雪夫距离就变成了求曼哈顿距离。
因为所求的是曼哈顿距离,所以可以两个维度分别单独考虑,对于第一维\(A_i+A_j\),容易想到这\(n^2\)个点中从小到大第 \(\lfloor \frac{n^2}{2} \rfloor+1\)个点到其他点的距离之和最小,可以二分这个点的大小\(x\),然后对每个\(A_i\)二分找到有多少个数\(A_j\)使得\(A_i+A_j<x\),找出第 \(\lfloor \frac{n^2}{2} \rfloor+1\)个点后,同样可以这样对每个点二分计算贡献来计算答案,对第二维再做一遍类似的过程就可以了。
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;++i)
#define per(i,n,a) for (int i=n;i>=a;--i)
#define sz(x) ((int)(x).size())
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
typedef pair<int,int> pii;
#define ll long long
const int inf=1e9;
const int mod=1e9+7;
const int N=1e5+10;
int n;
int a[N];
ll b[N];
bool ck(int x){
ll cnt=0;
rep(i,1,n) if(a[i]<=x){
int pos = lower_bound(a+1,a+n+1,x-a[i])-a-1;
cnt+=pos;
}
return cnt<=1ll*n*n/2;
}
bool ck1(int x){
ll cnt=0;
rep(i,1,n) {
int pos = upper_bound(a+1,a+n+1,a[i]-x)-a;
cnt+=n+1-pos;
}
return cnt<=1ll*n*n/2;
}
int main(){
ios::sync_with_stdio(false);
//freopen("in","r",stdin);
cin>>n;
rep(i,1,n) cin>>a[i];
sort(a+1,a+n+1);
rep(i,1,n) b[i]=(b[i-1]+a[i])%mod;
int l=1,r=1e9;
while(l<=r){
int mid=l+r>>1;
if(ck(mid)) l=mid+1;
else r=mid-1;
}
ll ans=0;
rep(i,1,n){
if(a[i]<=r){
int pos=upper_bound(a+1,a+n+1,r-a[i])-a-1;
ans=(ans-b[pos])%mod;
ans=(ans-1ll*pos*a[i]%mod)%mod;
ans=(ans+1ll*r*pos%mod)%mod;
int cpos=n-pos;
ans=(ans+1ll*a[i]*cpos%mod+b[n]-b[pos]-1ll*r*cpos%mod)%mod;
}else{
ans=(ans+b[n]+1ll*n*a[i]%mod-1ll*r*n%mod)%mod;
}
}
l=-1e9,r=1e9;
while(l<=r){
int mid=l+r>>1;
if(ck1(mid)) l=mid+1;
else r=mid-1;
}
rep(i,1,n){
int pos=lower_bound(a+1,a+n+1,a[i]-r)-a;
int cpos=n+1-pos;
ans=(ans+1ll*cpos*r%mod-1ll*a[i]*cpos%mod+b[n]-b[pos-1])%mod;
if(pos>0) ans=(ans-1ll*(pos-1)*r%mod+1ll*a[i]*(pos-1)%mod-b[pos-1])%mod;
}
ans=(ans%mod+mod)%mod;
cout<<ans<<endl;
return 0;
}