演算法學習筆記 14 離散化操作

2021-10-18 16:35:05 字數 3872 閱讀 8736

離散化是一種輔助解決問題的操作,當問題中涉及的資料範圍非常大,但是實際使用到的資料是比較少的。並且問題的求解是和它範圍裡的其它資料有關係的,那麼可以將這些可能使用到的資料放到一起,排序去重,就將它們對映到了乙個新的較小的範圍裡。

例如,下面幾個數是在(−∞

,∞

)(-\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上的...