Задача [B-A'] 2: Аккуратные распилы Условие задачи...
Criado em: 3 de setembro de 2025
Respondido usando GPT-5 Thinking por Chat01
Criado em: 3 de setembro de 2025
Respondido usando GPT-5 Thinking por Chat01
Задача [B-A'] 2: Аккуратные распилы
Условие задачи (pdf)
На факультете геометрии одного мерзкого, флегматичного и тоскливого института хранятся иллюстрации выпуклых многоугольников, координаты вершин которых являются целыми числами. Администрация факультета считает иллюстрацию тем лучше, чем больше существует способов аккуратно распилить ее на две части.
Будем считать аккуратным распилом многоугольника отрезок, соединяющий две его вершины таким образом, что обе получившиеся части исходного многоугольника непусты (имеют положительную площадь), а также площади обеих частей являются целыми числами.
Студентам в качестве экзамена была дана задача — найти количество аккуратных распилов данного выпуклого многоугольника. Разумеется, студенты мерзкого института не справились с этой задачей. Теперь ее придется решить вам.
Формат входных данных
Первая строка содержит одно целое число
n
(
4
≤
n
≤
200
000
) — количество вершин многоугольника.
Каждая из следующих
n
строк содержит два целых числа
x
i
и
y
i
(
−
10
9
≤
x
i
,
y
i
≤
10
9
) — координаты
i
-й вершины многоугольника.
Гарантируется, что многоугольник является выпуклым, а вершины перечислены в порядке его обхода по или против часовой стрелки.
Формат выходных данных
Выведите одно целое число — количество аккуратных распилов данного многоугольника.
Примеры
Входные данные
4
0 0
1 0
1 1
0 1
Выходные данные
0
Входные данные
4
0 0
2 0
2 2
0 2
Выходные данные
2
Входные данные
6
1 0
5 1
8 4
7 8
2 7
-1 3
Выходные данные
3
Примечания
В первом примере существует два распила, однако они не являются аккуратными, так как делят многоугольник на части с площадью
1
2
.
Во втором примере оба распила являются аккуратными.
Иллюстрация к третьему примеру приведена ниже. На ней показаны аккуратные распилы, а также посчитаны площади полученных многоугольников.
python# Let's implement the counting algorithm in Python to test the logic on sample inputs. def count_neat_cuts(points): n=len(points) def cross(i,j): x1,y1=points[i] x2,y2=points[j] return x1*y2 - y1*x2 # parities for each point tp=[((x&1)<<1)| (y&1) for x,y in points] # prefix parities of edges (i,i+1) for i=0..n-2 pref=[0]*n for i in range(n-1): pref[i+1]=(pref[i]+(cross(i,i+1)&1))&1 total_parity=(pref[n-1]+(cross(n-1,0)&1))&1 if total_parity==1: return 0 det=[[0]*4 for _ in range(4)] for a in range(4): ax=(a>>1)&1; ay=a&1 for b in range(4): bx=(b>>1)&1; by=b&1 det[a][b]=( (ax&by) ^ (ay&bx) ) # parity of cross mod2 # build counts for initial suffix j in [2..n-1] counts=[[0,0] for _ in range(4)] for j in range(2,n): counts[tp[j]][pref[j]]+=1 ans=0 for i in range(n-1): # i=0..n-2 for t in range(4): p_needed=pref[i]^det[tp[i]][t] ans+=counts[t][p_needed] if i==0: # subtract edge (0, n-1) if n-1>=2: if pref[n-1]==(pref[0]^det[tp[0]][tp[n-1]]): ans-=1 if i+2<=n-1: counts[tp[i+2]][pref[i+2]]-=1 return ans # test print(count_neat_cuts([(0,0),(1,0),(1,1),(0,1)])) # example 1 expected 0 print(count_neat_cuts([(0,0),(2,0),(2,2),(0,2)])) # example 2 expected 2 print(count_neat_cuts([(1,0),(5,1),(8,4),(7,8),(2,7),(-1,3)])) # example 3 expected 3
Ниже — короткое решение и линейный алгоритм.
Пусть — выпуклый многоугольник с целочисленными вершинами в порядке обхода.
Если площадь нецелая (т.е. нечётна), «аккуратных распилов» нет вовсе.
Действительно, сумма двух целых площадей не может дать нецелое число.
Для диагонали (берём , не ребро) удвоенная площадь части, ограниченной цепочкой и диагональю , равна
Нам нужна лишь чётность .
Введём префиксы по чётности
Тогда
и условие «площадь целая» эквивалентно
Значит, достаточно знать для вершины её тип из четырёх: .
Держим «хвост» допустимых для текущего : это индексы
(пара — ребро, её отдельно вычтем при ).
В этом хвосте считаем вершины по двум признакам:
тип и значение — итого таблица .
Для фиксированного и типа нужные — это те, у кого , где — заранее подготовленная таблица чётностей для пар типов. Суммируем по всем типам соответствующие счётчики из хвоста — получаем вклад текущего . Переходим к : удаляем из хвоста вершину . Весь подсчёт — операций на шаг, итого .
Если нечётно — сразу печатаем 0.
cpp#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; if (!(cin >> n)) return 0; vector<long long> x(n), y(n); for (int i = 0; i < n; ++i) cin >> x[i] >> y[i]; auto cross2_parity = [&](int i, int j)->int { // [vi, vj] mod 2, в F2 вычитание = сложение int xi = x[i] & 1, yi = y[i] & 1; int xj = x[j] & 1, yj = y[j] & 1; return (xi & yj) ^ (yi & xj); }; // Префиксы по чётности, без последнего ребра (n-1,0) vector<int> p(n, 0); for (int i = 0; i+1 < n; ++i) { p[i+1] = p[i] ^ cross2_parity(i, i+1); } // чётность 2*S(P) int total_parity = (p[n-1] ^ cross2_parity(n-1, 0)); if (total_parity & 1) { cout << 0 << "\n"; return 0; } // Тип вершины по (x mod 2, y mod 2) auto typeOf = [&](int i)->int { return ((x[i]&1) << 1) | (y[i]&1); // 0..3 }; int tp0 = typeOf(0); // Таблица det2 для пар типов int det2[4][4]; for (int a = 0; a < 4; ++a) { int ax = (a>>1)&1, ay = a&1; for (int b = 0; b < 4; ++b) { int bx = (b>>1)&1, by = b&1; det2[a][b] = (ax & by) ^ (ay & bx); } } // Счётчики в «хвосте» j in [2..n-1] long long cnt[4][2] = {}; for (int j = 2; j < n; ++j) { cnt[typeOf(j)][p[j]]++; } long long ans = 0; for (int i = 0; i < n-1; ++i) { int ti = typeOf(i); for (int t = 0; t < 4; ++t) { int need = p[i] ^ det2[ti][t]; ans += cnt[t][need]; } // убрать ребро (0, n-1), если сочтено if (i == 0) { int tlast = typeOf(n-1); if (p[n-1] == (p[0] ^ det2[tp0][tlast])) ans--; } // сдвигаем «окно»: выкидываем j = i+2 if (i+2 < n) { cnt[typeOf(i+2)][p[i+2]]--; } } cout << ans << "\n"; return 0; }
Код считает только по чётностям, поэтому нигде не переполняется (даже при ), а ответ хранится в long long.
Задача [B'-B] 6: Оценка за курс
Условие задачи (pdf)
Миша изучает курс по алгоритмам в Высшей Школе Битовых операций. Итоговая оценка за курс вычисляется следующим образом: в каждом из
n
домашних заданий студент решает какое-то количество задач, затем студент выбирает любые
k
различных домашних заданий и вычисляет побитовое И количества сданных задач из выбранного множества домашних заданий.
К концу семестра Миша решил повысить свою оценку. В каждом домашнем задании у него уже решено сколько-то задач, а также осталось время чтобы решить ещё
m
задач из любых домашек (можно дорешивать как одну домашку, так и несколько разных, возможно, он дорешает меньшее количество задач). К сожалению, Миша круто решает задачи по алгоритмам, но не может понять какие задачи ему стоит дорешать. Ваша задача определить его максимальную оценку за курс.
1
10
.
Формат входных данных
В первой строке входных данных содержится три целых числа
n
,
m
,
k
(
1
≤
k
≤
n
≤
2
⋅
10
5
,
0
≤
m
<
2
30
) — количество домашних заданий, количество задач, которые Миша успеет дорешать и количество домашних заданий, которые учитываются в вычислении итоговой оценки.
Во второй строке содержится
n
целых чисел
a
1
,
a
2
,
…
,
a
n
(
0
≤
a
i
<
2
30
) — количество задач, которые решил Миша в каждом из домашних заданий.
Формат выходных данных
Выведите одно целое число — максимальную итоговую оценку за курс после того как Миша дорешает не больше
m
задач.
Примеры
Входные данные
5 2 3
6 6 15 7 4
Выходные данные
7
Входные данные
3 2 3
4 4 4
Выходные данные
4
Примечания
В первом примере Миша может дорешать по одной задаче из первого и второго домашнего задания и выбрать для подсчета итоговой оценки домашние задания с номерами
1
,
2
и
4
Во втором примере Мише выгодно не дорешивать задачи.