Problem C. Manhattan Distance
主要算法:扫描线 二分
来源: XIII Samara Regional Intercollegiate Programming Contest Russia, Samara, March 29, 2020
题意 :给你二维平面上的n个点,每个点之间都存在一个曼哈顿距离,要求你求出第k小的曼哈顿距离\((2\leq n\leq 100000)\)
假如你有两个点\((x_i,y_i)\),\((x_j,y_j)\)那他们的曼哈顿距离\(d=|x_i-x_j|+|y_i-y_j|\)
思路:就是把难处理的所有点之间的曼哈顿距离转化为可以二维扫描处理的 切比雪夫距离
切比雪夫距离\(d=max(|x_i-x_j|,|y_i-y_j|)\)
而我们只需要把原来的\((x_i,y_i)\),\((x_j,y_j)\)变成新的\((x_i+y_i,x_i-y_i)\),\((x_j+y_j,x_j-y_j)\),只要计算新的点的切比雪夫距离,就可以得到原来点的曼哈顿距离
而对于新的切比雪夫距离,我们要看到他的max的性质,在有了这个max的条件下,我们就可以去二分答案,二分这个距离,对于每一个当前需要判断的距离,我去找有几对点的\(x\)和\(y\)都满足距离小于当前二分出来的距离
要处理\(x\)和\(y\)都满足的情况有多少对,我们可以固定\(x\)再动态的去看\(y\)。
具体就是,我们对于新的点先按照\(x\)排序,然后再利用双指针从左往右的遍历对于每一个点的\(x\)来说前面合理的区间,确保 区间内的点\(x\)是合法的,然后再用树状数组动态维护区间里面的所有点的\(y\),利用树状数组特有的区间求和统计\(y\)也在范围里面的点的个数,这样求出来的点数,就是对于当前的点所有比他小的满足条件的点的个数
最后就是判断是否符合再二分答案即可
代码
int n,k;
vector<array<int,2> > a;
vector<int > all;
int find(int x) //离散化
{
return lower_bound(all.begin(), all.end(), x) - all.begin() + 1;
}
int c[N+5]; //树状数组
int lowbit(int x)
{
return x & -x;
}
void add(int pos, int val)
{
while(pos <= n) // 不能越界
{
c[pos] += val;
pos += lowbit(pos);
}
}
int getsum(int x)
{
int ans = 0;
while (x >= 1)
{
ans = ans + c[x];
x = x - lowbit(x);
}
return ans;
}
void init(int n)
{
for(int i=0;i<=n;i++) c[i]=0;
}
bool check(int mid) //二分查找
{
init(N);
int num=0;
for(int i=0,j=0;i<n;i++)
{
while(a[i][0]-a[j][0]>mid&&j<i)
{
add(find(a[j][1]),-1);
j++;
}
int up=find(a[i][1]+mid);
if(all[up-1]!=a[i][1]+mid) up--;
int down=find(a[i][1]-mid);
num+=getsum(up)-getsum(down-1);
add(find(a[i][1]),1);
}
return num>=k;
}
void solve()
{
cin>>n>>k;
for(int i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
a.push_back({x+y,x-y});
all.push_back(x-y);
}
sort(all.begin(),all.end());
all.erase(unique(all.begin(),all.end()),all.end());
sort(a.begin(),a.end());
int l=0,r=inf;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
}
else l=mid+1;
}
cout<<l<<"\n";
}
参考: Gym-102569C Manhattan Distance 曼哈顿距离的转换 二分 - MQFLLY - 博客园 (cnblogs.com)