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個整數,表示可照亮彼此的燈塔對的數量。內存
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×106 這個數據量是很大的,所以咱們必需要用快排對橫座標排序,時間度認爲是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%的樣例,說明咱們的算法依然不夠快,依然要進行優化,那麼在這裏效果最顯著的方法
一個就是改歸併排序爲樹狀數組可能相對要更快一些,另外一個就是優化手寫的快速排序算法,能夠採用生成隨機數或者採用三數取中等等優化方法使得咱們手寫的快排更趨近穩定,可是因爲時間緣由,小編沒有嘗試下去。