Корневые эвристики
Корневые эвристики
Корневые эвристики — это обобщённое название методов и структур данных, которые опираются на факт:
если разделить множество из элементов на блоки по элементов, то блоков будет не больше .
Корневые эвристики
Корневые эвристики — это обобщённое название методов и структур данных, которые опираются на факт:
если разделить множество из элементов на блоки по элементов, то блоков будет не больше .
Ключевая идея:
«маленьких объектов много, но они маленькие; больших объектов мало»
Классический пример — факторизация за корень.
Если — «большой» делитель числа , то ему соответствует «маленький» делитель:
То есть больших делителей мало, потому что каждому большому соответствует малый.
Такая логика часто переносится на другие объекты: строки, вершины графа, типы запросов, веса предметов и т.д.
Нужно онлайн поддерживать множество строк и обрабатывать операции:
Разделим строки на:
Здесь выбирается как , где — суммарная длина всех строк.
Тогда длинных строк мало: не больше .
Дан граф из вершин и рёбер. Нужно посчитать количество циклов длины 3 (треугольников).
Вершина тяжёлая, если её степень больше , иначе лёгкая.
Тяжёлых вершин мало: их не больше .
Разбираем по числу тяжёлых вершин в треугольнике:
Нет тяжёлых вершин
В треугольнике есть ребро , третья вершина лежит в пересечении/объединении соседей.
Так как степени маленькие, получаем ограничение порядка .
Одна тяжёлая
Аналогично: есть «лёгкое» ребро → оценка тоже .
Две тяжёлые
Фиксируем ребро , способов .
Тяжёлых вершин всего → снова .
Три тяжёлые
Тяжёлых вершин мало, перебор также укладывается.
Итого треугольников «не так много»: порядка .
Отсортируем вершины по (степень, номер).
Дальше перебираем пути вида в правильном порядке и проверяем наличие ребра .
Есть предметов с весами , причём:
Стандартная динамика работает за .
Если есть различных положительных весов с суммой , то:
откуда:
То есть различных весов не больше .
Если вес встречается раз, разложим в суммы степеней двойки и заменим эти предметов на предметы весов:
Так сохраняются все суммы вида для .
После такого преобразования число предметов становится порядка , а дальше запускаем стандартную динамику.
Примечание: решение рюкзака можно сильно ускорить с помощью bitset.
long long count_triangles(int n, vector<vector<int>> &g) {
vector<int> deg(n);
for (int v = 0; v < n; v++) deg[v] = (int)g[v].size();
auto better = [&](int a, int b) {
if (deg[a] != deg[b]) return deg[a] < deg[b];
return a < b;
};
// sort vertices by (degree, id)
vector<int> order(n);
iota(order.begin(), order.end(), 0);
sort(order.begin(), order.end(), better);
// sort adjacency lists in the same order
for (int v = 0; v < n; v++) {
sort(g[v].begin(), g[v].end(), better);
}
vector<int> cnt(n, 0);
long long ans = 0;
for (int v : order) {
// mark all "forward" neighbors of v
for (int u : g[v]) {
if (!better(v, u)) break;
cnt[u] = 1;
}
// count triangles v - u - w where v<u<w in this order
for (int u : g[v]) {
if (!better(v, u)) break;
for (int w : g[u]) {
if (!better(u, w)) break;
ans += cnt[w];
}
}
// unmark
for (int u : g[v]) {
if (!better(v, u)) break;
cnt[u] = 0;
}
}
return ans;
}