Post #17
1.95K
This post (sticker, poll or similar) has no web preview. Open in Telegram
- 🔥 10
БО @bminaiev_upsolving
This post (sticker, poll or similar) has no web preview. Open in Telegram
n вершин, каждая характеризуется числами (x_i, A_i, B_i). Ребро между вершинами i и j стоит A_i * x_j + B_i. Формула получилась немного не симметричная, но на самом деле (A_i, B_i) такие, что расстояние в одну и другую сторону получаются одинаковые. Нужно на таком графе построить максимальное остовное дерево.TL=1s, а сумма n может быть 5*10^5. Поэтому было бы неплохо делать каждую итерацию за линию, тогда суммарная сложность будет O(n log n). Оказывается так можно сделать!Ax+B. Отсортируем их по A, будем хранить стек, и добавлять их по одной. Потом посортируем все запросы по x и указателем будем искать нужную прямую. Все сортировки можно сделать заранее.O(n).i, если она равна нулю, то записать first[i] = 1, а иначе second[n - i] = 1. Если их перемножить, то в res[n - len] + res[n + len] будет записано сколько пар (0, 1) на расстноянии ровно len. C(2 * n, n) / (n + 1), но и как C(2 * n, n) - C(2 * n, n + 1). Тогда не нужно делить, получается чуть быстрее!2n" считается именно таким образом? Есть какое-то понятное отображение между последовательностями и выборобом каких-то позиций?20 раз решить такую подзадачу. Есть последовательность a из нулей и единиц длиной 2*10^5. Нужно для каждого shift посчитать сколько существует таких i, что a[i] != a[i + shift].shift нас просят просто взять битсет, сдвинуть его, проксорить, и посчитать количество единиц. Нам,конечно, нужно сделать суммарно 20 * (2*10^5)^2 / 2 = 4*10^11 операций. Но если поделить это число на 64, то получается всего 6 миллиардов, звучит уже не так и страшно. https://codeforces.com/contest/2268/submission/3927652248*n*k*4+q*k, при том что n=10^5, q=3*10^5, k=800, т.е. делает примерно 3 миллиарда операций (и выделяет примерно столько же памяти). TL=4s и если это реально авторское, то мое уважение. TimeLimit. https://pastebin.com/2VqTiGtU. Я специально старался не особо оптимизировать на этом этапе (например, выделял много памяти), зато хотелось написать без багов.for j in 0..k {
dp[i + 1][1][j + 1] = dp[i + 1][1][j + 1].max(dp[i][0][j] + cur_val);
}avx, но ему тут явно не до этого. Например, он скорее всего вообще не понимает, что dp[i + 1] и dp[i] это разные участки памяти, поэтому можно все векторизовать. Еще, скорее всего он каждый раз делает "переход по ссылке", чтобы понять где находится dp[i + 1]. Поэтому я решил вынести dp[i + 1] в отдельный новый двухмерный массив, заполнить его, а потом скопировать в старых трехмерный. Локально это ускорило на пару секунд, но я все еще получил TimeLimit. https://pastebin.com/Rm4anpsaWA вместо TL. Правда, работала программа на этом тесте 3897ms, так что даже если исправить WA, то не факт, что решение работает достаточно быстро.AC за 3571ms! 🎉 https://pastebin.com/t31jUaTUperf top -p <pid>, было видно, что много времени тратится в циклах типа:for left_cnt in 0..=k {
best = best.max(dp_left[left_len][0][left_cnt] + dp[0][k - left_cnt]);
}fn fast_conv(a: &[i32], b: &[i32]) -> i32 {
let mut res = i32::MIN;
let n = a.len();
assert_eq!(n, b.len());
for i in 0..n {
res = res.max(a[i] + b[n - 1 - i]);
}
res
}.copy_from_slice(...). https://pastebin.com/8RPHsZ3S. 1685ms.perf record / perf report показывают что-то вроде "100% cpu тратится в функции solve, 14% вот в этих местах, а еще 86% непонятно где". Как понять где конкретно тратится время и на что?codex научить меня это делать и подсказать, что и где еще не эффективно. По дороге я еще ради прикола завайбкодил что-то вроде локального godbolt, но так и не понял, почему он чем-то лучше чем perf report.#[target_feature(enable = "avx2")]. Тоже помогло на 100-200мс.copy_from_slice, где можно сразу посчитать новое правильное значение динамики. ndp[j] = dp0[j].max(dp1[j] + cur_val) и вызвать ее с правильными slice. Помогло довольно сильно!update_pref_max, которая по сути делала a[i] = a[i].max(a[i - 1]), всегда вызывалась на массиве, который вначале возрастал, а потом убывал. Поэтому можно было найти позицию максимума и сделать a[max_pos..].fill(max_value).update_pref_max вообще и перейти от динамики "взял k отрезков" к "взял не более k отрезков".Vec<Vec<...>>, а просто в отсортированный Vec<...> (на сервере столько же работает, локально быстрее).AC за 367ms. Так что можно считать, что авторы выставили TimeLimit с запасом :)O(n * logn * быстро).v в другую компоненту, посмотрим на минимум на всем массиве, и возьмем тот из двух элементов, который не равен текущей компоненте.randseed пока не найдем такой, который решает тест за 30000 операций. Такое решение я написал на контесте и получил ~55 баллов.randseed. Немного подождем и у нас 70 баллов!P2 во время контеста я написал решение с асимптотикой O(n * b^2). В подзадаче P3 ограничения были:n <= 10^5b <= 1000n по всем тестам не больше 10^6rayon = "1.12" в [dependencies] в Cargo.toml. А в самой задаче считать инпут в вектор, сделать на нем into_par_iter().map(|input| solve(input)).collect() и все!
Point с double x, y, добавляют в случайные места программы EPS при различных вычислениях и вообще ничего не бояться по жизни. Я же очень сильно боюсь так делать и стараюсь писать все вычисления в целых числах. Иначе, при получении WA, очень сложно понять это бага в коде или нужно просто поменять EPS.A и B лежащих на ней, а другая точками C и D. Нужно найти некоторую точку Q, которая лежит на их пересечении. Запишем позицию Q в виде A + u * (B - A). Удобно, что получив значение u, можно заодно понять где конкретно она лежит по отношению к A и B. Например, если 0 <= u <= 1, то она лежит на отрезке AB. Если u > 1, то на продолжении луча AB.u? Мне кажется удобно думать об этом с геометрической точки зрения. Посмтрим на треугольники ADC и BCD. Их площади относятся как u к 1 - u. Площадь треугольника ADC это просто векторное произведение AD * AC (с точностью до 1/2). Отсюда получаем, что u = ADC / (BCD + ADC).u продолжает работать даже для u < 0 и u > 1, когда какие-то из площадей отрицательные. А если BCD + ADC = 0, то это обозначает, что AB и CD параллельны.C = 10^9, то векторное произведение порядка C^2, а u это дробь, где числитель и знаменатель порядка C^2. Если мы захотим такие дроби сравнивать, то нужен будет тип, который вмещает C^4 (int128).