【Tsinghua OJ】燈塔(LightHouse)問題


描述

海上有許多燈塔,為過路船只照明。從平面上看,海域范圍是[1, 10^8] × [1, 10^8] 。

圖一

(圖一)

如圖一所示,每個燈塔都配有一盞探照燈,照亮其東北、西南兩個對頂的直角區域。探照燈的功率之大,足以覆蓋任何距離。燈塔本身是如此之小,可以假定它們不會彼此遮擋。

(圖二)

若燈塔A、B均在對方的照亮范圍內,則稱它們能夠照亮彼此。比如在圖二的實例中,藍、紅燈塔可照亮彼此,藍、綠燈塔則不是,紅、綠燈塔也不是。

現在,對於任何一組給定的燈塔,請計算出其中有多少對燈塔能夠照亮彼此。

輸入

共n+1行。

第1行為1個整數n,表示燈塔的總數。

第2到n+1行每行包含2個整數x, y,分別表示各燈塔的橫、縱坐標。

輸出

1個整數,表示可照亮彼此的燈塔對的數量。

輸入樣例

3
2 2
4 3
5 1

輸出樣例

1

限制

對於90%的測例:1 ≤ n ≤ 3×105

對於95%的測例:1 ≤ n ≤ 106

全部測例:1 ≤ n ≤ 4×106

燈塔的坐標x, y是整數,且不同燈塔的x, y坐標均互異

1 ≤ x, y ≤ 10^8

提醒

注意機器中整型變量的范圍,C/C++中的int類型通常被編譯成32位整數,其范圍為[-231, 231 - 1],不一定足夠容納本題的輸出。

時間:2s,內存:256MB

 


 

 

【solution】

很容易的,我們能將這道題和逆序對聯系起來。

原因在於A和B能夠相互照亮的條件(假設A(x)<B(x))就是B在A的右上方。注意原題注明了:不同燈塔的x, y坐標均互異。

所以只要我們首先將所有點按照x坐標排序,接下來在y坐標中統計所有“非逆序對”或者說“順序對”,再求和,就可以了。

而問題的關鍵就在於在這樣的數據規模下如何快速的統計出“順序對”的個數。

如果只是單純的O(n^2)的算法顯然是效率不夠的。

 

那,如何從向量中快速的統計出“順序對”的個數?

這里大概思考之后可以聯想到 歸並排序。

歸並排序的關鍵也就是用分治的策略,將原問題一分為二的遞歸下去,最后的關鍵步驟只是將左右兩個有序的區間合並起來即可。

而很快會發現,在合並的過程中似乎我們就可以順便統計“順序對”的個數。

考慮這樣的一種情形:

目前左右兩個有序區間,分別有指針 i, j 指向該區間下一個要合並的元素。

當a[i] < a[j]的時候(不可能相等,原題目已經說明),此時 a[i] 就要被合並。

而 a[j] 以及所有右區間大於 a[j] 的元素 其實都與 a[i] 構成“順序對”。

統計完后,i 指向 i++ 的元素,且區間也都是沒有重疊部分的,所以也並不會重復計數。

這樣在歸並排序合並的同時,也將所有的“順序對”無重復無遺漏地記錄了下來。

算法復雜度也就是O(nlogn)的。

 

源碼如下:

 1 #include <stdio.h>
 2 
 3 #define L 1000005
 4 
 5 int y[L], le[L], ri[L];
 6 long ans = 0;
 7 
 8 void qsort(int a[], int b[], int l, int r)
 9 {
10     int i, j, x, t;
11     i = l; j = r; x = a[i + ((j - i)>>1)];
12     do
13     {
14         while (x > a[i]) i++;
15         while (x < a[j]) j--;
16         if (i <= j)
17         {
18             t = a[i]; a[i] = a[j]; a[j] = t;
19             t = b[i]; b[i] = b[j]; b[j] = t;
20             i++; j--;
21         }
22     } while (i <= j);
23     if (i < r) qsort(a, b, i, r);
24     if (j > l) qsort(a, b, l, j);
25 }
26 
27 void Merge(int l, int mi, int r)
28 {
29     int i, j, k, n1 = mi - l + 1, n2 = r - mi;
30     const int MAX = 100000005;
31 
32     for (i = 1; i <= n1; i++) le[i] = y[l + i - 1];
33     for (i = 1; i <= n2; i++) ri[i] = y[mi + i];
34 
35     le[n1 + 1] = MAX; ri[n2 + 1] = MAX;
36 
37     i = 1; j = 1;
38     for (k = l; k <= r; k++)
39     {
40         if (le[i] > ri[j]) y[k] = ri[j++];
41         else
42         {
43             y[k] = le[i++];
44             ans += long(n2) - j + 1;
45         }
46     }
47 
48 
49 }
50 
51 void Merge_Sort(int l, int r)
52 {
53     int mi;
54     if (l == r) return;
55     mi = l + ((r - l)>>1);
56     Merge_Sort(l, mi);
57     Merge_Sort(mi + 1, r);
58     Merge(l, mi, r);
59 }
60 
61 int main(void)
62 {
63     int n, x[L];
64     scanf("%d", &n);
65     for (int i = 1; i <= n; i++)
66     {
67         scanf("%d %d\n", &x[i], &y[i]);
68     }
69 
70     qsort(x, y, 1, n);
71 
72     Merge_Sort(1, n);
73 
74     printf("%ld\n", ans);
75 
76     return 0;
77 }

要注意題目中的提示部分,用 int 類型可能不足以容納結果數字。對 x 的排序使用的是快排(快速排序)。

此程序可以通過 Tsinghua OJ 上 95%的數據,也就是 n > 1 * 10^6 的那個點無法通過(即使改了代碼中的 L 也不行,Runtime error (exitcode: 11)),暫無解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM