ระยะห่าง (distance)

1125

สามารถสรุปใจความโจทย์ได้ว่า กำหนดจุดทั้งหมด NN จุด โดยจุดที่ ii อยู่ที่พิกัด (Xi,Yi)(X_i, Y_i) จงหา i=1Nj=1N(XiXj+YiYj)\sum\limits_{i=1}^{N} \sum\limits_{j=1}^{N} \left( |X_i-X_j| + |Y_i-Y_j| \right)

สังเกตว่าเราสามารถแยกคิดผลบวกในแกน X และแกน Y ได้ นั่นคือ เราต้องการหา

(i=1Nj=1NXiXj)+(i=1Nj=1NYiYj)\left( \sum\limits_{i=1}^{N} \sum\limits_{j=1}^{N} |X_i-X_j| \right) + \left( \sum\limits_{i=1}^{N} \sum\limits_{j=1}^{N} |Y_i-Y_j| \right)

หากเราต้องการหาผลรวมของระยะห่างระหว่างจุดทุกคู่โดยสนใจเพียงแกนเดียว การคำนวณทุกคู่จุดตรง ๆ จะต้องใช้เวลาถึง O(N2)O(N^2) ซึ่งไม่ทันสำหรับกรณีที่ NN มีค่ามากถึง 500000500\,000

เราสามารถลดระยะเวลาการทำงานได้โดยการเรียงลำดับพิกัดแต่ละแกนจากน้อยไปมาก จะเห็นว่า ระยะทางแต่ละคู่จุดสามารถแยกออกเป็นส่วน ๆ ได้

ยกตัวอย่าง หากพิกัดแกน X เป็น 2,3,5,7,112, 3, 5, 7, 11 จะมีคู่จุดที่นำมาแบ่งส่วนระยะทางได้ดังนี้

  • 23|2-3|
  • 25|2-5| แยกเป็น 23+35|2-3|+|3-5|
  • 27|2-7| แยกเป็น 23+35+57|2-3|+|3-5|+|5-7|
  • 211|2-11| แยกเป็น 23+35+57+711|2-3|+|3-5|+|5-7|+|7-11|
  • 35|3-5|
  • 37|3-7| แยกเป็น 35+57|3-5|+|5-7|
  • 311|3-11| แยกเป็น 35+57+711|3-5|+|5-7|+|7-11|
  • 57|5-7|
  • 511|5-11| แยกเป็น 57+511|5-7|+|5-11|
  • 711|7-11|

ดังนั้น ผลรวมของระยะห่างแต่ละคู่ คิดได้จากการรวมระยะทาง

  • 23|2-3| ทั้งหมด 44 ครั้ง
  • 35|3-5| ทั้งหมด 66 ครั้ง
  • 57|5-7| ทั้งหมด 66 ครั้ง
  • 711|7-11| ทั้งหมด 44 ครั้ง

เข้าด้วยกัน ได้คำตอบของแกน X เป็น 423+635+657+4711=444|2-3| + 6|3-5| + 6|5-7| + 4|7-11| = 44

กล่าวโดยทั่วไป ระยะห่างระหว่างจุดที่ ii กับ i+1i+1 (1i<N1 \leq i < N) จะต้องนำมาคิดทั้งหมด i(Ni)i \cdot (N-i) ครั้ง เพราะว่าระยะช่วงนั้นจะถูกคิดจากการจับคู่จุดที่อยู่ทางด้านซ้ายทั้งหมด ii จุดกับจุดที่อยู่ทางด้านขวา NiN-i จุดเข้าด้วยกันได้ i(Ni)i \cdot (N-i) คู่

Implementation

ในภาษา C:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

typedef long long ll;

int n;
int x[500000];
int y[500000];

int cmpfunc(const void *a, const void *b) { return (*(int *)a - *(int *)b); }

int main(void) {
  scanf("%d", &n);
  for (int i = 0; i < n; i++)
    scanf("%d %d", &x[i], &y[i]);

  ll dist = 0;

  qsort(x, n, sizeof(int), &cmpfunc);
  qsort(y, n, sizeof(int), &cmpfunc);
  ll sumx = 0;
  ll sumy = 0;
  for (int i = 1; i < n; i++) {
    int diffx = (x[i] - x[i - 1]);
    sumx += diffx * i;
    int diffy = (y[i] - y[i - 1]);
    sumy += diffy * i;

    dist += sumx + sumy;
  }

  printf("%lld", dist);
  return 0;
}