松鼠聚会(洛谷-P3964)

题目描述

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

每个小松鼠的家可以用一个点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。

30%的数据,0 ≤ N ≤ 1000

100%的数据,0 ≤ N ≤ 100000; −10^9 ≤ x, y ≤ 10^9

输入输出格式

输入格式:

第一行是一个整数N,表示有多少只松鼠。接下来N行,第i行是两个整数x和y,表示松鼠i的家的座标

输出格式:

一个整数,表示松鼠为了聚会走的路程和最小是多少。

输入输出样例

输入样例#1:

6
-4 -1
-1 -2
2 -4
0 2
0 3
5 -2

输出样例#1:

20

输入样例#2:

6
0 0
2 0
-5 -2
2 -2
-1 2
4 0

输出样例#2:

15

思路:

本题实质是给出 n 个点,找出一个点 x,然后使得其他 n-1 个点到 x 的切比雪夫距离最小,之后求距离和的最小值

而需要求 n 个点切比雪夫距离的和时,由于要穷举其他 n-1 个点到当前点的距离,即计算:

max(\Delta x_1,\Delta y_1)+max(\Delta x_2,\Delta y_2)+...+max(\Delta x_n,\Delta y_n)

可以发现,每次计算距离都要取 max,每次都是一个 O(n) 的计算过程,总时间复杂度可达 O(n^2),复杂度太高,而曼哈顿距离只有求和与取绝对值两种运算,因此我们可以考虑将切比雪夫距离转化为曼哈顿距离,然后利用前缀和优化,进而降低时间复杂度。

设 dist_{(i,j)} 为从 i 到 j 曼哈顿距离,那么有:\sum_{i=1}^ndis(i,j),复杂度仍是 O(n^2)

进一步化简,有:

\sum_{i=1}^ndis(i,j)=dis(1,j)+dis(2,j)+...+dis(n,j)

同时以 dis(i,j) 中的一部分 \Delta x=|x_i-x_j| 举例化简,有:

\sum_{i=1}^n\Delta x=|x_1-x_j|+|x_2-x_j|+...+|x_j-x_j|+|x_{j+1}-x_j|+...+|x_n-x_j|

若先将横座标处理为递增的,那么易得 |x_j-x_j| 前的部分是可以继续拆绝对值化简的,|x_{j+1}-x_j| 后的部分是 \geq 0 的,因此进一步可化简为:

\sum_{i=1}^j(x_j-x_i)+\sum_{i=j+1}^n(x_i-x_j)

可以发现上式子是一个前缀和,而 \Delta y 部分与上式同理,因此我们只需要维护有序状态下 \sum_{i=1}^kx_i\sum_{i=1}^ky_i 的值即可。

此时,各步骤的时间复杂度如下:

  • 座标变换:O(n)
  • 前缀和处理:O(n)
  • 枚举每个点:O(n)
  • 计算一个点的答案:O(log n)

总时间复杂度即为:O(nlog n),相较于之前的 O(n^2),已有了极大的优化。

源代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL multMod(LL a,LL b,LL mod){ a%=mod; b%=mod; LL res=0; while(b){if(b&1)res=(res+a)%mod; a=(a<<=1)%mod; b>>=1; } return res%mod;}
LL quickMultPowMod(LL a, LL b,LL mod){ LL res=1,k=a; while(b){if((b&1))res=multMod(res,k,mod)%mod; k=multMod(k,k,mod)%mod; b>>=1;} return res%mod;}
LL quickPowMod(LL a,LL b,LL mod){ LL res=1; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1; } return res; }
LL getInv(LL a,LL mod){ return quickPowMod(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-6;
const int MOD = 1000000000+7;
const int N = 100000+5;
const int dx[] = {0,0,-1,1,1,-1,1,1};
const int dy[] = {1,-1,0,0,-1,1,-1,1};
using namespace std;

struct Node {
    LL x, y;
} node[N];
int n, x[N], y[N];
LL sum1[N], sum2[N];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        x[i] = node[i].x = a + b;
        y[i] = node[i].y = a - b;
    }
    sort(x + 1, x + n + 1);
    sort(y + 1, y + n + 1);

    for (int i = 1; i <= n; i++){
        sum1[i] = sum1[i - 1] + x[i];
        sum2[i] = sum2[i - 1] + y[i];
    }

    LL res=1ll << 62;
    for (int i = 1; i <= n; i++) {
        int pos = lower_bound(x + 1, x + n + 1, node[i].x) - x;
        LL sum = sum1[n] - sum1[pos] - node[i].x * (n - pos) + node[i].x * pos - sum1[pos];
        pos = lower_bound(y + 1, y + n + 1, node[i].y) - y;
        sum += sum2[n] - sum2[pos] - node[i].y * (n - pos) + node[i].y * pos - sum2[pos];
        res = min(res, sum);
    }
    printf("%lld\n", res / 2);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章