DestinHistoire

 

BZOJ-3170 [Tjoi2013]松鼠聚会(切比雪夫距离转曼哈顿距离)

题目描述

  草原上住着一群小松鼠,每个小松鼠都有一个家。时间长了,大家觉得应该聚一聚。但是草原非常大,松鼠们都很头疼应该在谁家聚会才最合理。

  每个小松鼠的家可以用一个点 \((x,y)\) 表示,两个点的距离定义为点 \((x,y)\) 和它周围的 \(8\) 个点 \((x-1,y),(x+1,y),(x,y-1),(x,y+1),(x-1,y+1),(x-1,y-1),(x+1,y+1),(x+1,y-1)\) 距离为 \(1\)

  数据范围:\(0\leq n\leq 10^5,-10^9\leq x,y\leq 10^9\)

分析

  题意简述:在 \(n\) 个点中找到一个点 \(x\),使其他 \(n-1\) 个点到 \(x\)切比雪夫距离 之和最小。求距离和的最小值。

  假设 \(A(x_1,y_1),B(x_2,y_2)\)\(A,B\) 两点的曼哈顿距离为:

\[\begin{align}d(A,B)&=|x_1-x_2|+|y_1-y_2|\\&=\max\{x_1-x_2+y_1-y_2,x_1-x_2+y_2-y_1,x_2-x_1+y_1-y_2,x_2-x_1+y_2-y_1\}\\&(把绝对值拆开,能够得到四个值,这四个值中的最大值是两个非负数之和,即曼哈顿距离)\\&=\max(|(x_1+y_1)|-(x_2+y_2)|,|(x_1-y_1)-(x_2-y_2)|)\end{align} \]

  容易发现,这就是 \((x_1+y_1,x_1-y_1),(x_2+y_2,x_2-y_2)\) 两点之间的切比雪夫距离。

  所以 将每一个点 \((x,y)\) 转化为 \((x+y,x-y)\),新坐标系下的切比雪夫距离即为原坐标系下的曼哈顿距离

  同理,\(A,B\) 两点的切比雪夫距离为:

\[d(A,B)=\max\{|x_1-x_2|,|y_1-y_2|\}=\Big|\frac{x_1+y_1}{2}-\frac{x_2+y_2}{2}\Big|+ \Big|\frac{x_1-y_1}{2}-\frac{x_2-y_2}{2} \Big| \]

  这就是 \(\big(\frac{x_1+y_1}{2},\frac{x_1-y_1}{2}\big),\big(\frac{x_2+y_2}{2},\frac{x_2-y_2}{2}\big)\) 两点之间的曼哈顿距离。

  所以 将每一个点 \((x,y)\) 转化为 \(\big(\frac{x+y}{2},\frac{x-y}{2}\big)\),新坐标系下的曼哈顿距离即为原坐标系下的切比雪夫距离

  回到本题,读入切比雪夫意义下的坐标并化为曼哈顿意义下的坐标,因为有除 \(2\) 可能有小数,先不除 \(2\),最后把距离除 \(2\) 是一样的。

  设 \(d(i,j)\) 为曼哈顿距离下点 \(i\) 到点 \(j\) 的曼哈顿距离。设当前枚举的终点为 \(j\),则 \(ans=\displaystyle\sum\limits_{i=1}^{n}d(i,j)\),时间复杂度为 \(O(n^2)\)

  把和式展开:

\[\sum\limits_{i=1}^{n}d(i,j)=d(1,j)+d(2,j)+\cdots+d(n,j) \]

  取 $ d(i,j)$ 中的第一维坐标 \(|x_i-x_j|\) 化简:

\[\sum\limits_{i=1}^{n}\Delta x=|x_1-x_j|+|x_2-x_j|+\cdots+|x_j-x_j|+|x_{j+1}-x_j|+\cdots+|x_n-x_j| \]

  如果先将横坐标处理为递增的,可知 \(|x_j-x_j|\) 前面的式子一定可以拆绝对值化简,\(|x_{j+1}-x_j|\) 及以后的式子一定 \(\geq 0\),即:

\[\sum\limits_{i=1}^{n}\Delta x=\sum\limits_{i=1}^{j}(x_j-x_i)+\sum\limits_{i=j+1}^{n}(x_i-x_j) \]

  维护递增状态下 $x_i $ 和 \(y_i\) 的前缀和,\(\displaystyle\sum\limits_{i=1}^{j}x_j\) 直接使用 \(j\times x_j\) 计算即可。

  \(\text{dis}(i,j)\) 的第二维 \(\Delta y\) 可同理化简。

  也就是说,分别对 \(x_i,y_i\) 排序,处理成递增的序列。

  枚举每一个终点,把这个终点的横坐标 \(x_i\),纵坐标 \(y_i\) 在排好序的数组中的位置利用二分查找求出来,可以快速计算所有点到终点的距离之和。

  时间复杂度 \(O(n\log n)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const long long INF=2e18;
int n;
int x[N],y[N],tempx[N],tempy[N];
long long sumx[N],sumy[N];
long long solve(int i)
{
    int posx=lower_bound(tempx+1,tempx+1+n,x[i])-tempx;
    int posy=lower_bound(tempy+1,tempy+1+n,y[i])-tempy;
    return 1ll*posx*x[i]-sumx[posx]+sumx[n]-sumx[posx]-1ll*(n-posx)*x[i]+
           1ll*posy*y[i]-sumy[posy]+sumy[n]-sumy[posy]-1ll*(n-posy)*y[i];
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int X,Y;
        scanf("%d%d",&X,&Y);//转化为曼哈顿距离意义下的坐标
        x[i]=tempx[i]=X+Y;//tempx与tempy是经过排序后得到的有序数组
        y[i]=tempy[i]=X-Y;//x与y存第i个点的坐标
    }
    sort(tempx+1,tempx+1+n);
    for(int i=1;i<=n;i++)
        sumx[i]=sumx[i-1]+tempx[i];
    sort(tempy+1,tempy+1+n);
    for(int i=1;i<=n;i++)
        sumy[i]=sumy[i-1]+tempy[i];
    long long ans=INF;
    for(int i=1;i<=n;i++)
        ans=min(ans,solve(i));
    printf("%lld\n",ans/2);
    return 0;
}

posted on 2020-12-01 22:59  DestinHistoire  阅读(57)  评论(0编辑  收藏  举报

导航