轉載自洛谷[https://www.luogu.org/blog/yihan/unordered](https://www.luogu.org/blog/yihan/unordered)
這是啥?
我們知道,在c++11中出現了一些有用的容器,其中包括了兩(三)個非常實用的容器:unordered_map & unordered_set / unordered_multiset
其實現的操作與map&set/multiset 差不多,只是我們知道map和set實現的是 O(logn)O(logn) 進行實現一個映射,其中內置的實現是紅黑樹。 多數時候已經非常優秀了,但是往往會有一些題用不到有序的序列(我們知道set內部是有序的),反而需要更快的速度。這個時候 可以選擇自己打hash。但是對於yijan這種懶癌晚期患者自然想要實用 unordered_map & unordered_multiset 實現 O(1)O(1) 查詢。
如果你會使用,可以直接跳過這部分
首先是定義。如果在c++11環境下,可以很方便寫出來:
#include<unordered_set> #include<unordered_map> #include<iostream> using namespace std; unordered_set<int> S1; unordered_multiset<int> S2; unordered_map<int,int> M; int main(){ S1.insert(6); S2.insert(7); M[233333] = 666666; cout << M[233333]; }
但是我們NOIP系列競賽顯然是不滋瓷c++11 的!怎么辦?
那自然是有奇技淫巧的!:(據說是沒有問題的)
#include<tr1/unordered_set> #include<tr1/unordered_map> #include<iostream> using namespace std; using namespace tr1; unordered_set<int> S1; unordered_multiset<int> S2; unordered_map<int,int> M; int main(){ S1.insert(6); S2.insert(7); M[233333] = 666666; cout << M[233333]; }
然后就可以在非11環境用了!(比如luoguide 選擇c++不選11就能跑!)
好了,說了那么多,下面進入正題((
下面我們要! 卡掉 unordered_map !
但是說的簡單,如何卡掉?首先我們知道,只要是hash函數,如果多次碰撞,自然速度 O(n) -> O(n^2)O(n)−>O(n2)
如果對探索過程不敏感可以跳過這一段
首先,讓我們去unorderedmap的源文件:On github
我們得知道這個東西的實現原理,里面我們可以看到這一句話,這個hashtable 顯然調用了 _Mod_range_Hashing
和 _Prime_rehash_policy
從這里我們就可以大概知道這個東西的實現過程:首先hash這個數據,然后對這個hash值取模放入unordered_map。
對_Prime_rehash_policy
的探索(hashtable_c++0x.cc)我們可以發現一個數組:__prime_list
。並且此時你又可以摸清其中的一部分實現:當這個map過大的時候,它就會resize其本身。當拋開這個我們先把這個質數表找到。
然后呢 打開了(hashtable-aux.cc)里面就可以看到這個數組了!
好了,我們現在找到了這個數組,以及掌握了基本實現。但是我們還是應當搞清楚內置hash實現是什么。事實上是使用了std::hash
在cppreference里寫到:
無序關聯容器 std::unordered_set 、 std::unordered_multiset 、 std::unordered_map 、 std::unordered_multimap 以該模板 std::hash 的特化為默認哈希函數。
到這里,我們就可以卡掉它了。並不是所有的質數都能做到這一點,而只有特殊的一兩個。由於博主實在懶得花時間去尋找這個質數,下面引用cf博文一句話:
for gcc 6 or earlier, 126271 does the job, and for gcc 7 or later, 107897 will work.
我們現在使用的編譯器多位6or earlier 所以 126271 是一個非常合適的質數。
真正卡掉一個代碼
luogu有一個題P1102 A-B數對這是一個普通map都能ac的水題。我們來對比一下unordered_set & unordered_map
這是份AC代碼:
然后下圖中,上面的是采用map,下面的采用unordered_map
可以看到,無論空間和時間都是unordered后優秀。
Hack It!
我們用前面說過的那個卡掉代碼的質數來做輸入文件。輸入的數字為126271的1~200000倍。然后分別使用map和unordered_map 來跑。看看結果怎樣?(在其他oj創建的題目,速度應該也差不多)
測試數據下載:input_file
沒錯,你沒有看錯,足足跑了4s!
不被hack?
自己寫一個hash函數!
當然我相信多數時候是不會有人刁難hack你unordered_map的。。除非是cf比賽。。。(ccf造這種數據那我也沒啥辦法了)
具體自己寫hash函數可以自己參考我發的資料。在此不再累贅。
總結
unordered_map/set 確實是個好東西!
如果noip中發現復雜度沒錯,
盡量使用map
畢竟ccf啥都能卡(已經死了個shortest path fake algorithm)
下場cf別忘了去hack別人哦
關於卡掉hash 參考了 cf 的文章: neal's blog(已經獲得博主關於轉載的許可)
noip考場上自然還有很多這樣的可以拿來騙分用的STL容器。關於可持久平衡樹也可以參考我的一篇題解可持久平衡樹四十行實現(板子題MLE)
當然,pbds黨肯定認為hash這種東西直接調啊QWQ那我也沒辦法了。。