на задаче 977F - Consecutive Subsequence у меня Тл решение с unordered_map 37945413 но точно такое же решение с map 37989920 у меня прошло. Это случаем не баг, ведь unordered_map работает быстрее map . Или я ошибаюсь?
UPD:
Нашел крутые материалы про unordered_map:
Интересно. Действительно, отличие только в замене
unordered_map<int, pair<int, int>>
наmap<int, pair<int, int>>
. Однако если добавить к ключам случайный шум (например,a[i] ^ 23901755
вместоa[i]
), то проходит: 37990845. Работает не всегда, если шум меняет только младшие биты, то не помогает: 37990781 (TL на одном из следующих тестов).Кажется, что в этой конкретной версии GCC очень плохой хэш для ключей
int
и случаются тонны коллизий, из-за чего решение начинает вместо 0.3 секунд работать >2 секунд. Ну или кто-то заморочился и подобрал специальный тест с коллизиями против конкретной версии (сомневаюсь, так как там вроде генератор довольно простой).У меня локально с версией 7.3.0 не воспроизводится. Если перепослать на CF под более новым GCC, то снова воспроизводится: 37990911 и 37990919.
greencis, а с какой идеей был сделан взлом — просто большой тест или антихэш?
Антихэш. Я стараюсь сделать так, чтобы все ключи попали в как можно меньшее число бакетов, чтобы доступ к элементам длился максимально долго. В компиляторах GCC хэш от числа -- это само число, а номер бакета определяется как
(key % bucket_count)
. Зная количество бакетов на максимальном тесте, легко подобрать соответствующие числа.В C++11 и C++14 расширение хэш-таблицы происходит немного по-другому, нежели в C++17: видимо, константа отличается. А решения на MS C++ я ломать
пока не умею: там целые числа хэшируются при помощи специальной функции из нескольких арифметических операций, которую не так просто обратитьуже умею, обращение совсем простое.А не подкинете функцию? Что-то я за 2 минуты не нашёл. Мне казалось, что её было довольно легко сломать тоже.
Конечно. В файле VC\include\xhash можно найти следующий код:
Любопытный факт, кстати: 127773·16807 + 2836 = 231 - 1.
UPD: попытка загуглить число 16807 выдаёт ссылку на статью про ГСЧ Лемера.
1) Насколько я помню кол-во бакетов степень двойки, то есть мы по факту хотим чтобы младшие несколько битов совпадали. Поэтому можно от хеша брать только последние биты. Тестил таким кодом (16 для примера):
Пусть мы знаем y f(y) = 0, тогда f(y + 1) = 16807 (почти всегда)
Аналогично f(y + 2) == 2 * 16807 почти всегда.
Остается перебрать(или предпосчитать) нулевые хеши.
А, так это же просто умножение ключа на число 16807 по модулю 231 - 1 без long long. Получается, для обращения хэш-функции достаточно домножить значение на , и мы получим те числа, которые нам нужны.
Да, видимо, так гораздо проще, чем то, что я написал :)
Why so much dislikes?
what's the point, considering you solved it for yourself and english speakers won't see russian comments?
Oh shit...
Actually, I found a new cool blog.
Good amount of discussion on this same topic can be found here. Read all comments.
Oh) thank you!
The worst case time complexity of operation in unordered_map is O(n) rather than the expected O(1),moreover the complexity of unordered_map is amortised O(1) ,whereas for map is O(log n). Similar thing had happened to me earlier.
In which cases, unordered_map works O(N)? Could tell me about this?
Sorry,I don't know any specific case as such. But I am sure there are set of test cases which can make them happen everytime as the hash function which unordered_map uses is known ans thus the cases which produces more and more collisions are bound to produce TLE verdict.
https://mirror.codeforces.com/blog/entry/62393
Thank you for awesome blog!