Тяжёлые и лёгкие вершины
Сумма степеней равна 2m, поэтому вершин со степенью больше корня — меньше корня. Отсюда треугольники за m корень из m.
3 мин
Корень берут не только от длины массива. В графах его берут от степеней вершин, и работает это на одном наблюдении.
Наблюдение
Сумма степеней всех вершин равна . Значит, вершин со степенью больше не может быть больше - иначе сумма степеней превысила бы .
Назовём такие вершины тяжёлыми, остальные лёгкими. Получаются два утверждения противоположной природы:
- лёгкую вершину можно перебрать целиком - это ;
- тяжёлых мало, поэтому про них можно позволить себе предподсчёт.
Дальше остаётся разобрать случаи и убедиться, что ни в одном не осталось дорогой ветки.
Ориентация рёбер по степени
Есть вариант того же приёма, в котором делить вершины на два сорта не приходится. Направим каждое ребро от вершины меньшей степени к вершине большей, а при равных степенях - от меньшего номера к большему.
Тогда исходящая степень любой вершины не больше . Доказательство в одну строчку: если у исходящая степень , то все её целей имеют степень не меньше , значит сумма степеней не меньше , а она равна .
Сравнение по паре «степень, потом номер» задаёт строгий порядок, поэтому циклов в ориентированном графе не возникает.
Треугольники за
После ориентации каждый треугольник становится путём с ребром и находится ровно один раз: у трёх вершин треугольника есть единственная наименьшая по этому порядку.
for (int u = 0; u < n; u++) {
for (int v : outgoing[u]) mark[v] = 1;
for (int v : outgoing[u])
for (int w : outgoing[v]) if (mark[w]) triangles++;
for (int v : outgoing[u]) mark[v] = 0; // тем же списком, не memset
}
Внутренний перебор стоит
Очистка пометок тем же списком, которым их ставили, - не мелочь. memset на весь массив внутри цикла по вершинам превращает в , и это самая частая ошибка в этой задаче.
Чтобы разложить треугольники по вершинам, достаточно вместо одного счётчика увеличить три - именно потому, что каждый треугольник найден один раз, а не трижды.
Прибавление соседям
Классическая задача: «прибавить всем соседям вершины » и «узнать число в вершине ». В лоб прибавление стоит , и вершина с миллионом соседей всё ломает.
| операция | лёгкая вершина | тяжёлая вершина |
|---|---|---|
| прибавить соседям | честно, | запомнить: pending[v] += x |
| узнать значение | плюс сумма pending по тяжёлым соседям |
то же самое |
У любой вершины тяжёлых соседей не больше , поэтому обе операции стоят .
Общие соседи
Запрос «сколько общих друзей у и » разбирается по трём случаям:
- обе лёгкие - слияние двух отсортированных списков, ;
- одна тяжёлая - проход по списку лёгкой с проверкой пометки, ;
- обе тяжёлые - ответ берётся из таблицы, посчитанной заранее.
Таблица считается одним проходом: для каждой вершины перебираются пары тяжёлых среди её друзей, и каждая такая пара получает от единицу. Стоит это .
Порог не обязан быть ровно : он выбирается так же, как размер блока, - из равенства двух стоимостей. Если предподсчёт про тяжёлые дороже перебора лёгких, порог сдвигают.