ACM/ICPC 之 快排+归并排序-记录顺序对(TSH OJ-LightHouse(灯塔))

TsingHua OJ 上不能使用<algorithm>头文件,所以须要手写快排(刚开始写的时候本身就出了不少问题....),另外本题须要在给横坐标排序后,须要记录纵坐标的顺序对的数量,所以,最快的算法貌似只有归并排序或者树状数组的方法进行顺序对的查找和记录了,时间度为O(nlogn),另外此前须要一次对横坐标的排序,这里用快排。算法

 

灯塔(LightHouse)数组


描述

海上有许多灯塔,为过路船只照明。ide

如图一所示,每一个灯塔都配有一盏探照灯,照亮其东北、西南两个对顶的直角区域。探照灯的功率之大,足以覆盖任何距离。灯塔自己是如此之小,能够假定它们不会彼此遮挡。优化

若灯塔A、B均在对方的照亮范围内,则称它们可以照亮彼此。好比在图二的实例中,蓝、红灯塔可照亮彼此,蓝、绿灯塔则不是,红、绿灯塔也不是。ui

如今,对于任何一组给定的灯塔,请计算出其中有多少对灯塔可以照亮彼此。spa

输入

共n+1行。code

第1行为1个整数n,表示灯塔的总数。blog

第2到n+1行每行包含2个整数x, y,分别表示各灯塔的横、纵坐标。排序

输出

1个整数,表示可照亮彼此的灯塔对的数量。内存

Example

Input

3
2 2
4 3
5 1

Output

1

限制

对于90%的测例:1 ≤ n ≤ 3×105

对于95%的测例:1 ≤ n ≤ 106

所有测例:1 ≤ n ≤ 4×106

灯塔的坐标x, y是整数,且不一样灯塔的x, y坐标均互异

1 ≤ x, y ≤ 10^8

时间:2 sec

内存:256 MB

提示

注意机器中整型变量的范围,C/C++中的int类型一般被编译成32位整数,其范围为[-231, 231 - 1],不必定足够容纳本题的输出。

 


 

 

解题思路:

  乱序的坐标对咱们解题是没有帮助的,所以咱们首先应该想到对横坐标(纵坐标)作一次排序,而后考虑纵坐标(横坐标)。

在这里我先对横坐标进行排序,而后从小到大对纵坐标的要求进行概括,咱们能够发现:若是存在两个灯塔A(x1,y1),B(x2,y2),那么x1<x2时,当且仅当y1<y2时,A B两灯塔才能相互beacon(照亮),所以,这道题能够转化为当横坐标顺序肯定时,去记录纵坐标的顺序对数量。

  例如A(1,2),B(2,4),C(3,5),那么显然ABC中任两灯塔间能够相互beacon,其对数就是三对。

  ——咱们用顺序对来描述就是:A B C顺序摆放,其中AB,AC,BC的纵坐标(<2,4>,<2,5>,<4,5>)各为一个顺序对,所以有三对灯塔能够相互beacon,这与咱们的分析是一致的。

 

时间度分析:

  那么在分析完这道题目后,咱们就须要作两件事情,第一件事就是对横坐标进行一次排序,第二件事就是利用某种算法计算出横坐标排序后,纵坐标的顺序对的数量。

  在题目中给出 所有测例:1 ≤ n ≤ 4×10这个数据量是很大的,所以咱们必需要用快排对横坐标排序,时间度认为是O(nlogn),第二件事中,联系到逆序对的记录,咱们能够用到的最快算法有归并排序和树状数组,这里咱们试用归并排序进行顺序对记录,时间度也认为是O(nlogn)。

  如下是实现Code:

 1 #include<stdio.h>
 2 
 3 #define MAX 4000005
 4 long ans;
 5 
 6 struct Light{
 7     int x, y;
 8 }l[MAX];
 9 
10 int tmp[MAX];
11 
12 /*快排*/
13 void quickSort(int low, int high)
14 {
15     int i = low;
16     int j = high;
17     Light x = l[low];    //设置一个基准点
18     do{
19         while (l[i].x < x.x) i++;    //Let l[i].x >= x
20         while (l[j].x > x.x) j--;    //Let l[j].x <= x    
21         if(i <= j){    //SWAP
22             Light t = l[i];
23             l[i] = l[j];
24             l[j] = t;
25             i++; j--;
26         }
27     } while (i <= j); //使得p两侧知足 左<=p,右>=p
28     if(i < high) quickSort(i, high);
29     if(j > low ) quickSort(low, j);
30 }
31 
32 void merge(int low, int mid, int high)
33 {
34     int s = low, t = mid + 1, k = low;
35     while (s <= mid && t <= high){
36         if (l[s].y < l[t].y){
37             ans += high - t + 1;    //顺序对-右侧未放入的date数量
38             tmp[k++] = l[s++].y;
39         }
40         else tmp[k++] = l[t++].y;
41     }
42     if (s == mid + 1)    while (k <= high) tmp[k++] = l[t++].y;
43     else                while (k <= high) tmp[k++] = l[s++].y;
44     //COPY
45     for (int i = low; i <= high; i++) l[i].y = tmp[i];
46 }
47 
48 /*归并排序*/
49 void mergeSort(int low, int high)
50 {
51     if (low < high){
52         int mid = (low + high) / 2;
53         mergeSort(low, mid);
54         mergeSort(mid + 1, high);
55         merge(low, mid, high);
56     }
57 }
58 
59 int main()
60 {
61     int n;
62     scanf("%d", &n);
63     for (int i = 0; i < n; i++)
64         scanf("%d%d", &l[i].x, &l[i].y);
65     quickSort(0, n - 1);//横坐标快排
66     mergeSort(0, n - 1);//纵坐标归并排序并记录顺序对
67 
68     printf("%ld\n", ans);
69 
70     return 0;
71 }
小墨- -原创

 

  尽管如此,可是咱们依然只能A掉95%的样例,说明咱们的算法依然不够快,依然要进行优化,那么在这里效果最显著的方法

  一个就是改归并排序树状数组可能相对要更快一些,另外一个就是优化手写的快速排序算法,能够采用生成随机数或者采用三数取中等等优化方法使得咱们手写的快排更趋近稳定,可是因为时间缘由,小编没有尝试下去。

相关文章
相关标签/搜索