Подсчет треугольников#8616

Подсчет треугольников (Editorial)

Обозначим через количество различных треугольников, которые можно построить из стержней.

Условие существования треугольника. Пусть выбраны три стержня с длинами a < b < c. Тогда необходимое и достаточное условие существования треугольника имеет вид:

a + b > c

Поскольку — наибольшая сторона, остальные неравенства выполняются автоматически.

Вычислим несколько первых значений функции:

Рекуррентный подход. Заметим, что множество всех треугольников можно разбить на два подмножества:

Обозначим множество треугольников, содержащих сторону , через . Тогда:

T(n) = T(n - 1) + |S(n)|

Например

S(6) = \{ (4, 5, 6), (3, 5, 6), (2, 5, 6), (3, 4, 6) \}

Тогда

T(6) = T(5) \cup S(6)

Подсчет S(n). Рассмотрим треугольники вида (a, b, n), где a < b < n. Условие существования треугольника:

a + b > n

Зафиксируем и найдём возможные :

a > n - b

При этом a < b, следовательно:

n - b < a < b

Число подходящих значений равно:

b - (n - b + 1) = 2b - n - 1

Суммируем по всем допустимым , для которых 2b - n - 1 > 0, то есть:

b > \frac{n + 1}{2}

Пример. Вычислим значение , то есть найдем все треугольники вида (a, b, 6).

Необходио выполнение условия: b > (6 + 1) / 2 = 3. Учитывая что b < 6, возможными значениями для будут и .

Таким образом,

S(6) = \{ (3, 4, 6), (2, 5, 6), (3, 5, 6), (4, 5, 6) \}

Вычисление |S(n)|.

Случай 1: чётное. Пусть n = 2k. Тогда: b > \frac{2k + 1}{2} = k + \frac{1}{2}. Следовательно, значение может принимать значения: k + 1, k + 2, ..., 2k - 1 (так как b < n, то b < 2k и наибольшее значение может быть равным 2k - 1).

Теперь суммируем значения 2b - n - 1 = 2b - 2k - 1 для всех b~(k + 1 \le b \le 2k - 1).

Получаем:

|S(n)| = 1 + 3 + 5 + ... + (n - 3)

По формуле арифметической прогрессии:

|S(n)| = \frac{1 + (n - 3)}{2} \cdot \frac{n - 2}{2} = \frac{(n-2)^2}{4}

Случай 2: нечётное. Пусть n = 2k + 1. Тогда: b > \frac{2k + 2}{2} = k + 1. Следовательно, значение может принимать значения: k + 2, k + 3, ..., 2k - 1 (так как b < n, то b < 2k + 1 и наибольшее значение может быть равным ).

Теперь суммируем значения 2b - n - 1 = 2b - 2k - 2 для всех b~(k + 2 \le b \le 2k).

Получаем:

|S(n)| = 2 + 4 + 6 + ... + (n - 3)

По формуле арифметической прогрессии:

|S(n)| = \frac{2 + (n - 3)}{2} \cdot \frac{n - 3}{2} = \frac{(n - 1) (n - 3)}{4}

Таким образом получим рекуррентность:

Значение можно также выразить в явном виде:

T(n) = \begin{cases} \dfrac{1}{24}\, n(n - 2)(2n - 5), & \text{если } n \text{ чётно}, \\ \dfrac{1}{24}\, (n - 1)(n - 3)(2n - 1), & \text{если } n \text{ нечётно}. \end{cases}

Последовательность имеет производящую функцию

f(x)=\frac{x^4}{(1-x)^3 (1-x^2 )} = x^4 + 3x^5 + 7x^6 + 13x^7 + ...

Пример

Построим множество .

Поскольку содержит 2 + 4 = 6 элементов, то T(7) = T(6) + |S(7)| = 7 + 6 = 13.

Первые значения функции имеют вид:

Реализация алгоритма

Объявим массив для хранения результатов.

#define MAX 1000001
long long t[MAX];

Основная часть программы. Заполняем массив .

memset(t, 0, sizeof(t));
for(i = 4; i < MAX; i++)
  if (i % 2 == 0) t[i] = t[i - 1] + (i - 2) / 2 * (i - 2) / 2;
  else t[i] = t[i - 1] + (i - 1) * (i - 3) / 4;

Последовательно обрабатываем запросы.

while(scanf("%lld",&n), n >= 3)
  printf("%lld\n", t[n]);

Реализация алгоритма – формула

Реализация функции .

long long T(long long n)
{
  if (n % 2) return (n - 1) * (n - 3) * (2 * n - 1) / 24;
  return n * (n - 2) * (2 * n - 5) / 24;
}

Основная часть программы. Для каждого входного значения выводим .

while(scanf("%lld",&n), n >= 3)
  printf("%lld\n",T(n));