離散化是一種輔助解決問題的操作,當問題中涉及的資料範圍非常大,但是實際使用到的資料是比較少的。並且問題的求解是和它範圍裡的其它資料有關係的,那麼可以將這些可能使用到的資料放到一起,排序去重,就將它們對映到了乙個新的較小的範圍裡。
例如,下面幾個數是在(−∞
,∞
)(-\infty, \infty)
(−∞,∞)
範圍裡的,實際用到的數:
6 、−
10000
、114514
、1919、−
123、
1919
6、-10000、114514、1919、-123、1919
6、−100
00、1
1451
4、19
19、−
123、
1919
排序之後變成:
−
10000、−
123、6、
1919
、1919
、114514
-10000、-123、6、1919、1919、114514
−10000
、−12
3、6、
1919
、191
9、11
4514
去重之後變成:
−
10000、−
123、6、
1919
、114514
-10000、-123、6、1919、114514
−10000
、−12
3、6、
1919
、114
514它在陣列中的下標即可對應於一種離散化結果:
0 、1
、2、3
、4
0、1、2、3、4
0、1、2、
3、4在有些問題(比如下面涉及字首和操作的問題)裡,需要下標從1
11開始,所以離散化結果也常常用從1
11開始的:
1 、2
、3、4
、5
1、2、3、4、5
1、2、3、
4、5在這題裡,x
xx和詢問區間(l,
r)
(l, r)
(l,r
)都是從−10
9-10^9
−109
到1 09
10^9
109範圍裡的。首先陣列就沒法開這麼大,就算能開這麼大,那麼對n
nn(範圍105
10^5
105)個x
xx加上c
cc是可以操作的。但是後面因為要查詢區間和,所以要計算一下字首和,對109
10^9
109量級這麼長的陣列計算字首和的過程雖然是o(n
)o(n)
o(n)
的但是也是會超時的。
注意到新增數的數目n
nn,以及查詢的數目m
mm都是105
10^5
105量級的。也就是說雖然範圍很大,但是實際用到的數就是3×1
05
3 \times 10^5
3×10
5量級的(n
nn個x
xx,m
mm個l
ll和r
rr)。
由於在每個位置x
xx加c
cc之前,整個陣列是全0
00的,所以如果只記錄這些實際用到的位置,計算出來的字首和也是正確的。因為在原來的超長區間上「加0
00」的操作,和不做任何操作其實是沒有區別的。
所以這道題的解題流程就是先做離散化,再求字首和。對於每個插入(將x
xx位置增加c
cc的操作)記錄到add
陣列裡,對於每個查詢(查詢l
ll到r
rr的區間和)記錄到query
陣列裡。同時,每個x
xx、l
ll、r
rr都是在後面求字首和和區間和會用到的位置,所以把它們都加入到陣列nums
裡,排序並去重,那麼每個數所在的陣列下標就是一種離散化結果。由於這題裡涉及字首和,所以實際的離散化結果使用它所在的下標+1+1
+1,這樣就變成從1
11開始的了。
對於乙個要使用的值v
vv,想要知道它離散化之後的結果,其實就是去查一下它在nums
裡的下標是多少(這個過程可以用二分來快速查詢),然後+1+1
+1就是離散化結果了,這裡將這個「查詢離散化後的下標」的操作封裝到find()
函式裡。
從前面的分析可以知道,用到的數最多有3×1
05
3 \times 10^5
3×10
5個,每個數對應乙個唯一的離散化後的結果,所以字首和陣列也開這個量級的就可以了。
#include
#include
#include
using
namespace std;
typedef pair<
int,
int> pii;
// add存所有插入操作,query存所有查詢操作
// 因為要在離散化之後再處理這些操作,所以它們都要記錄下來
vector add, query;
// 離散化陣列
vector<
int> nums;
// 基於離散化後的資料的字首和陣列注意這裡開n + 2 * m範圍大小
const
int n =
3e5+10;
int s[n]
;// 給出原來範圍裡的數x,查詢它在離散化陣列x裡的下標再+1就是離散化結果
intfind
(int x)
// 注意這裡要把查詢到的下標+1,因為字首和陣列要求從1開始存資料
return l +1;
}int
main()
);// 由於x位置會用到,所以將這個位置記錄在待離散化陣列nums裡
nums.
push_back
(x);
}// 讀取m個「查詢區間和」的操作
for(
int i =
0; i < m; i ++))
;// 由於l和r位置會用到,所以將這兩個位置記錄在待離散化陣列nums裡
nums.
push_back
(l); nums.
push_back
(r);
}// 離散化:在nums裡排序去重,nums的下標即是從0開始的離散化結果
sort
(nums.
begin()
, nums.
end())
; nums.
erase
(unique
(nums.
begin()
, nums.
end())
, nums.
end())
;// 對於每個插入操作
for(
auto
& item: add)
// 計算字首和陣列,其長度其實其實就是nums長度(因為find離散化結果就是從1開始的下標)
for(
int i =
1; i <= nums.
size()
; i ++
) s[i]
+= s[i -1]
;// 對於每個查詢操作
for(
auto
& item: query)
return0;
}
基礎演算法學習 離散化
題目給出範圍很大但資料數量很少的一組資料,通過離散化將大的下標的值賦值給新的較小的連續的下標,從而講乙個範圍很大的資料合集裝進乙個小的容器中。vecrotalls 儲存所有待離散化的值 sort alls.begin alls.end 將alls裡的所有待離散化的值總小到大排序 alls.erase...
演算法筆記 離散化
離散化,就是把一些很離散的點給重新分配。舉個例子,如果乙個座標軸很長 1e10 給你1e4個座標,詢問某乙個點,座標比它小的點有多少。很容易就知道,對於1e4個點,我們不必把他們在座標軸上的位置都表示出來,因為我們比較有多少比它小的話,只需要知道他們之間的相對大小就可以,而不是絕對大小,這,就需要離...
學習筆記 離散化
離散化 是把無限空間中有限的個體對映到有限的空間中去,以此提高演算法的時空效率。通俗的說,離散化是在不改變資料相對大小的條件下,對資料進行相應的縮小。看乙個例子 acwing 802.區間和 假定有乙個無限長的數軸,數軸上每個座標上的數都是0。現在,我們首先進行 n 次操作,每次操作將某一位置x上的...