離散化實現|bug分析及解決
離散化:
對於一些應用例項,有時只會用到資料的相對大小,而不在意資料本身的大小
例:在區間塗色問題中,依次給區間塗色,後塗色的區間會覆蓋前區間。現在求剩下幾種顏色
可以發現,這個問題中,有用的就只是區間的相對位置關係,而不在於區間本身的大小。如:
[1, 3] 塗白色,[6, 7] 塗黑色
[1, 3] 塗白色,[10000006, 10000007] 塗黑色
最後都只有兩種顏色。但是若用線段樹維護,第一種情況要區間長度 7,第二種情況卻需要長度 10000007,太浪費時間空間
這裡離散化就派上用場了。離散化就是把很大的區間,對映到小的區間中
用到的STL函式:
- sort():排序。若原資料是無序的,就需要先排序之後再處理
- unique():把相鄰重複元素放到末尾,用不重複元素補上,返回不重複元素末尾地址。若原資料有序,則不重複元素也有序
- lower-boud():返回第一個不小於指定值的地址
離散陣列,左端點,右端點 int disc[MAXN], L[MAXN], R[MAXN]; 讀入資料 for (int k = 0; k < n; k++) { scanf("%d%d", L + k, R + k); disc[tot++] = L[k]; disc[tot++] = R[k]; } 若原資料無序,則需要排序後再去重 sort(disc, disc + tot); 去重,得到不重複元素的個數。此時陣列中前面的不重複元素保持有序 int size = (int)(unique(disc, disc + tot) - disc); 進行對映,拿該元素在disc陣列中的位置作為對映後的新下標 for (int i = 0; i < n; i ++) { L[i] = (int)(lower_bound(disc, disc + size, L[i]) - disc + 1); R[i] = (int)(lower_bound(disc, disc + size, R[i]) - disc + 1); }
離散化bug:
這樣離散的方式存在bug,我們舉個例子
[1, 10]、[1,6]、[7,10] ,剩下 2 種顏色
[1, 10]、[1,6]、[9,10] , 剩下 3 種顏色
但是離散化之後,區間全都變成
[1, 4]、[1,2]、[3,4] ,剩下 2 種顏色,很明顯有一些情況會出錯
分析:
我們維護了一些區間之後,儲存的不僅僅是哪些區域有顏色。從另一個角度看,同時也反映了哪些區間是空白的
如:[1,6]、[9,10] 兩個區間,就告訴了我們 [7, 8] 區間是空白的
然而,離散化的處理,就是把靠近的兩個數設成相鄰,把它們之間的空間設為 0 ,這樣會丟失空白的那部分資料
本身 6 與 9 之間有空白,離散化之後,變成 2 與 3。這與 6、7 的離散結果無異,空白資訊被丟失,對結果造成影響
解決:
有兩種解決方案
1、加點法:若兩個相鄰的數的差值大於 1 ,也就是說兩數之間有空白,就在它們之間插入一個點,用來體現空白
實現程式碼:
int disc[MAXN], L[MAXN], R[MAXN];
for (int k = 0; k < n; k++) { //讀入資料
scanf("%d%d", L + k, R + k);
disc[tot++] = L[k];
disc[tot++] = R[k];
}
sort(disc, disc + tot); //若原資料無序,則需要排序後再去重
int size = (int)(unique(disc, disc + tot) - disc); //去重,得到不重複元素的個數。此時陣列中前面的不重複元素保持有序
int temp = size; //加點,修復離散bug
for (int i = 1; i < temp; i++)
if (disc[i] - disc[i - 1] > 1)
disc[size++] = disc[i - 1] + 1;
sort(disc, disc + size);
for (int i = 0; i < n; i ++) { //進行對映,拿該元素在disc陣列中的位置作為對映後的新下標
L[i] = (int)(lower_bound(disc, disc + size, L[i]) - disc + 1);
R[i] = (int)(lower_bound(disc, disc + size, R[i]) - disc + 1);
}
2、在維護線段樹時,改變左右子樹的劃分方式
原來是: [l, mid]、[mid + 1, r] 改為:[l, mid]、[mid, r] 便可以解決