Спасибо за участие в раунде! Мы надеемся, что вам понравились задачи.
2234A - Евклид, последовательность, два числа
Идея: FairyWinx
Заметьте, что $$$(a_i \bmod a_{i + 1}) \lt a_{i + 1}$$$.
$$$a_{i + 2} = (a_i \bmod a_{i + 1}) \lt a_{i + 1}$$$ и $$$a_2 \le a_1$$$ означают, что последовательность $$$a$$$ должна быть невозрастающей. С другой стороны, есть только одна перестановка $$$b$$$, которая может быть невозрастающей.
t = int(input())
for tt in range(t):
n = int(input())
a = list(map(int, input().split()))
a.sort()
a = a[::-1]
valid = True
for i in range(2, n):
if a[i] != a[i - 2] % a[i - 1]:
print(-1)
valid = False
break
if valid:
print(a[0], a[1])
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> b[i];
}
sort(b.rbegin(), b.rend());
bool ok = true;
for (int i = 0; i < n - 2; i++) {
if (b[i + 2] != b[i] % b[i + 1]) {
ok = false;
break;
}
}
if (ok) {
cout << b[0] << " " << b[1] << "\n";
} else {
cout << -1 << "\n";
}
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
2234B - Палиндром, двенадцать, два слагаемых
Идея: Fakewave
tt = int(input())
for tc in range(tt):
n = int(input())
if n == 10:
print(-1)
elif n % 12 == 10:
print(22, n - 22)
else:
print(n % 12, n - (n % 12))
#include <bits/stdc++.h>
using namespace std;
void solve() {
long long n;
cin >> n;
if (n == 10) {
cout << "-1\n";
} else if (n % 12 == 10) {
cout << "22 " << n - 22 << "\n";
} else {
cout << n % 12 << " " << n - (n % 12) << "\n";
}
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234C - Сосуды, высоты, две версии (простая версия)
Идея: yanb0
Интуитивно, чем сосуд дальше от пустого, тем больше воды можно в нём уместить. Зафиксируйте пустой сосуд и попробуйте найти формулу в явном виде для максимальной высоты в каждом из оставшихся сосудов.
t = int(input())
for tc in range(t):
n = int(input())
h = list(map(int, input().split()))
ans = []
for s in range(n):
w1 = [0] * n
w2 = [0] * n
for i in range(1, n):
w1[(s + i) % n] = max(w1[(s + i - 1) % n], h[(s + i - 1) % n])
for i in range(1, n):
w2[(s + n - i) % n] = max(w2[(s + n - i + 1) % n], h[(s + n - i) % n])
w = [min(w1[i], w2[i]) for i in range(n)]
ans.append(sum(w))
print(*ans)
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> h(n);
for (int i = 0; i < n; i++) cin >> h[i];
for (int s = 0; s < n; s++) {
vector<int> w1(n), w2(n), w(n);
for (int i = 1; i < n; i++) {
w1[(s + i) % n] = max(w1[(s + i - 1) % n], h[(s + i - 1) % n]);
}
for (int i = 1; i < n; i++) {
w2[(s + n - i) % n] = max(w2[(s + n - i + 1) % n], h[(s + n - i) % n]);
}
for (int i = 0; i < n; i++) {
w[i] = min(w1[i], w2[i]);
}
cout << accumulate(w.begin(), w.end(), 0ll) << " ";
}
cout << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234D - Ксор, выражение, два бинарных числа
Идея: Fakewave
Пусть $$$A = a_1$$$, $$$B = a_{2^k + 1}$$$, $$$C = A \oplus B$$$.
Попробуйте выписать значения $$$a$$$, к примеру, для $$$k = 3$$$.
$$$A \oplus B = C$$$, $$$B \oplus C = A$$$, $$$C \oplus A = B$$$.
Посмотрим, как $$$a$$$ меняется пошагово:
a = [A, ?, ?, ?, ?, ?, ?, ?, B];a = [A, ?, ?, ?, C, ?, ?, ?, B];a = [A, ?, B, ?, C, ?, A, ?, B];a = [A, C, B, A, C, B, A, C, B].
Попробуйте найти и доказать закономерность.
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a(n), b(n);
char g;
for (int i = 0; i < n; ++i) {
cin >> g;
a[i] = g - '0';
}
for (int i = 0; i < n; ++i) {
cin >> g;
b[i] = g - '0';
}
vector<int> c(4);
for (int i = 0; i < n; ++i) {
int x = 2 * a[i] + b[i];
c[x]++;
}
if (k % 2) {
long long p = 0, q = 0, r = 0;
p = c[0] + c[1];
q = c[0] + c[3];
r = c[0] + c[2];
cout << (((1ll << k) + 1) / 3) * (p * (n-p) + q * (n - q) + r * (n - r)) << "\n";
} else {
long long p = 0, q = 0, r = 0;
p = c[0] + c[1];
q = c[0] + c[2];
r = c[0] + c[3];
cout << (((1ll << k) + 1) / 3) * (p * (n-p) + q * (n - q) + r * (n - r)) + p * (n - p) + q * (n - q) << "\n";
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
}
Есть довольно прямолинейное решение за $$$\mathcal{O}(nk \log k)$$$.
Прочитайте первые две подсказки к решению 1. Докажите, что каждое $$$a_i \in [A, B, C]$$$.
Для каждой пары битов $$$(x, y)$$$, посчитайте количество позиций $$$h$$$, для которых $$$h$$$-е биты $$$a_1$$$ и $$$a_{2^k+1}$$$ равны $$$x$$$ и $$$y$$$ соответственно. Продолжайте рекурсивно.
Используйте кэширование.
Пусть ответ на задачу, где $$$a_1 = A$$$, $$$a_{2^k+1} = B$$$ и $$$k = l$$$ — $$$F(l, A, B)$$$.
Тогда, раз мы записываем значение $$$A \oplus B$$$ посередине массива на первом шагу, а затем продолжаем рекурсивно, как для $$$k = l - 1$$$, $$$F(l, A, B) = F(l - 1, A, A \oplus B) + F(l - 1, A \oplus B, B) - [\text{произведение количеств битов } 0 \text{ и } 1 \text{ в } A \oplus B]$$$.
Ссылаясь на доказательство в решении 1, что все $$$a_i \in [A, B, C]$$$, для каждого $$$l$$$ значение функции требуется посчитать для различных пар $$$(A, B)$$$ максимум $$$6$$$ раз, а значит, если мы используем словарь для кэширования ответ $$$F$$$, мы сделаем не более $$$6k$$$ вызовов $$$F$$$, из-за чего асимптотика $$$\mathcal{O}(n + k \log k)$$$.
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, k, a, b, c;
string p, q, r;
int get(string &s) {
int a = 0, b = 0;
for (char c : s)
if (c == '0') a++;
else b++;
return a * b;
}
map<array<int, 4>, int> mp;
int solve(int l, int a, int b, int c) {
array<int, 4> t = {l, a, b, c};
if (mp.find(t) == mp.end()) {
if (l == 0) mp[t] = a + b;
else mp[t] = solve(l - 1, c, a, b) + solve(l - 1, b, c, a) - c;
}
return mp[t];
}
void test_case() {
cin >> n >> k >> p >> q;
r.clear();
for (int i = 0; i < n; i++)
r.push_back('0' + ((p[i] - '0') ^ (q[i] - '0')));
a = get(p);
b = get(q);
c = get(r);
mp.clear();
cout << solve(k, a, b, c) << '\n';
}
int32_t main() {
ios::sync_with_stdio(0); cin.tie(0);
int t; cin >> t;
while (t--) test_case();
}
2234E - Влад, Миша, два массива
Идея: Fakewave
Смотря только на массив $$$a$$$, как найти индекс $$$i$$$, для которого $$$p_i = 1$$$, т.е. минимум $$$p$$$?
Попробуйте придумать рекурсивное решение, работающее за $$$\mathcal{O}(n^2)$$$.
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Mod = 1e9 + 7, Max = 5e5 + 10;
vector<int> fact(Max), ifact(Max);
int binpow(int b, int p) {
if (p == 0) return 1;
if (p % 2 == 0) return binpow((b * b) % Mod, p / 2);
return (binpow(b, p - 1) * b) % Mod;
}
int C(int n, int k) {
return (fact[n] * ((ifact[k] * ifact[n - k]) % Mod)) % Mod;
}
int rec(int l, int r, vector<int> &b) {
if (r < l) {
return 1;
}
if (l == r) {
return (b[l] == 1 ? 1 : 0);
}
for (int d = 0; d < r - l + 1; d++) {
int i = d + l;
if ((i - l + 1) * (r - i + 1) == b[i]) {
return (((rec(l, i - 1, b) * rec(i + 1, r, b)) % Mod) * C(r - l, i - l)) % Mod;
}
i = r - d;
if ((i - l + 1) * (r - i + 1) == b[i]) {
return (((rec(l, i - 1, b) * rec(i + 1, r, b)) % Mod) * C(r - l, i - l)) % Mod;
}
}
return 0;
}
void solve() {
int n;
cin >> n;
vector<int> b(n);
for (int i = 0; i < n; i++) cin >> b[i];
int s = accumulate(b.begin(), b.end(), 0ll);
if (s != n * (n + 1) / 2) {
cout << "0\n";
return;
}
cout << rec(0, n - 1, b) << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
fact[0] = 1;
for (int i = 1; i < Max; i++) {
fact[i] = (fact[i - 1] * i) % Mod;
}
for (int i = 0; i < Max; i++) {
ifact[i] = binpow(fact[i], Mod - 2);
}
int t;
cin >> t;
while (t--) {
solve();
}
}
Будет скоро добавлено.
2234F - Сосуды, высоты, две версии (сложная версия)
Идея: yanb0
Прочитайте решение C. Попробуйте посчитать ответ, если пустой сосуд — сосуд $$$1$$$, затем каким-то образом быстро меняйте его, двигая пустой сосуд, чтобы он был сосудом $$$2$$$, $$$3$$$, и т.д.
Рассмотрите самое высокое соединение между сосудами.
Здесь не нужны продвинутые структуры данных. Используйте стек.
INF = float("inf")
tt = int(input())
for tc in range(tt):
n = int(input())
h = list(map(int, input().split()))
t = 0
for i in range(n):
if h[i] > h[t]:
t = i
ls = [0 for i in range(n)]
rs = [0 for i in range(n)]
sm = [(INF, 0)]
for ti in range(1, n):
i = (ti + t) % n
s = ls[i] + h[i]
c = 1
while sm[-1][0] <= h[i]:
s += sm[-1][1] * (h[i] - sm[-1][0])
c += sm[-1][1]
sm.pop()
sm.append((h[i], c))
ls[(i + 1) % n] = s
sm = [(INF, 0)]
for ti in range(1, n):
i = (t + n - ti) % n
s = rs[(i + 1) % n] + h[i]
c = 1
while sm[-1][0] <= h[i]:
s += sm[-1][1] * (h[i] - sm[-1][0])
c += sm[-1][1]
sm.pop()
sm.append((h[i], c))
rs[i] = s
ans = [ls[i] + rs[i] for i in range(n)]
print(*ans)
#include <bits/stdc++.h>
#define int long long
using namespace std;
using pii = pair<int, int>;
void solve() {
int n;
cin >> n;
vector<int> h(n);
for (int i = 0; i < n; i++) cin >> h[i];
int t = max_element(h.begin(), h.end()) - h.begin();
vector<int> ls(n), rs(n);
stack<pii> sm;
sm.push({1e18, 0});
for (int ti = 1; ti < n; ti++) {
int i = (ti + t) % n;
int s = ls[i] + h[i], c = 1;
while (sm.top().first <= h[i]) {
s += sm.top().second * (h[i] - sm.top().first);
c += sm.top().second;
sm.pop();
}
sm.push({h[i], c});
ls[(i + 1) % n] = s;
}
sm = stack<pii>();
sm.push({1e18, 0});
for (int ti = 1; ti < n; ti++) {
int i = (t + n - ti) % n;
int s = rs[(i + 1) % n] + h[i], c = 1;
while (sm.top().first <= h[i]) {
s += sm.top().second * (h[i] - sm.top().first);
c += sm.top().second;
sm.pop();
}
sm.push({h[i], c});
rs[i] = s;
}
for (int i = 0; i < n; i++) {
cout << ls[i] + rs[i] << " ";
}
cout << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}
2234G - Полоска, фишка, два игрока
Идея: yanb0
Придумайте решение за $$$\mathcal{O}(n^3)$$$.
ДП. Для каждой пары $$$(cell, strength)$$$ вы можете найти, выигрывает ли игрок, ходящий из этого положения, при оптимальной игре.
В этой игре не очень много проигрышных позиций. Почему?
Проигрышных позиций $$$(i, k)$$$ для фиксированного $$$k$$$ при $$$i \le n$$$ не больше $$$\big\lceil \frac{n}{k + 1} \big\rceil$$$, т.к. если позиция $$$(i, k)$$$ проигрышная, то все позиции $$$(i + 1, k), (i + 2, k), \ldots, (i + k, k)$$$ должны быть выигрышными, если они существуют. Тогда суммарно проигрышных позиций $$$(i, k)$$$ при $$$i \le n$$$ не больше $$$\mathcal{O}(n \log n)$$$.
Попробуйте проитерироваться по номеру клетки $$$i$$$ от $$$n$$$ до $$$1$$$ и в явном виде находить все проигрышные позиции.
Вам нужно придумать структуру данных, которая позволит вам оптимизировать решение до $$$\mathcal{O}(n \log^2 n)$$$.
Будем обозначать состояние игры, где фишка на клетке $$$i$$$ и имеет силу $$$k$$$ до использования вкусняшек как $$$(i, k)$$$. Нам нужно определить, выигрышная ли позиция $$$(1, 1)$$$.
Заметим, что проигрышных позиций $$$(i, k)$$$ для фиксированного $$$k$$$ при $$$i \le n$$$ не больше $$$\big\lceil \frac{n}{k + 1} \big\rceil$$$, т.к. если позиция $$$(i, k)$$$ проигрышная, то все позиции $$$(i + 1, k), (i + 2, k), \ldots, (i + k, k)$$$ должны быть выигрышными, если они существуют. Тогда суммарно проигрышных позиций $$$(i, k)$$$ при $$$i \le n$$$ не больше $$$\mathcal{O}(n \log n)$$$.
Будем итерироваться по номеру клетки $$$i$$$ от $$$n$$$ до $$$1$$$ и в явном виде находить все проигрышные позиции.
Давайте поддерживать множество $$$S$$$, содержащее все $$$k$$$, при которых нет проигрышной позиции $$$(j, k)$$$, где $$$i \lt j \le i + k$$$. Это можно делать с помощью очереди с приоритетом, добавляя событие "проигрышные позиции с силой $$$k$$$ на отрезке $$$[i + 1, i + k]$$$ закончились" на позицию $$$i - k - 1$$$, когда позиция $$$(i, k)$$$ проигрышная. (Также, это добавление событий делается изначально для позиций $$$(n + 1, 0), (n + 1, 1), \ldots, (n + 1, n)$$$, т.к. они все проигрышные, а силу можно ограничить сверху до $$$n$$$, не меняя игру.) Таких событий в очереди будет добавлено суммарно не более $$$\mathcal{O}(n \log n)$$$ (по количеству проигрышных позиций), а значит, запросов к очереди не более $$$\mathcal{O}(n \log n)$$$, т.к. в каждой клетке мы будем прочитывать и удалять только события, относящиеся к ней. Заметим, что только когда мы прочитываем такое событие, мы добавляем в $$$S$$$ один элемент, и добавлений в $$$S$$$ тоже не более $$$\mathcal{O}(n \log n)$$$.
Будем находить проигрышные позиции для клетки $$$i$$$ так: позиция $$$(i, k)$$$ проигрышная тогда и только тогда, когда для всех $$$0 \le d \le a_i$$$, среди позиций $$$(j, k + d)$$$ при $$$j \in [i + 1, i + k + d]$$$ нет проигрышных, т.е. тогда и только тогда, когда в $$$S$$$ есть все из $$$k, k + 1, \ldots, k + a_i$$$. Для быстрого нахождения таких $$$k$$$ используем следующую структуру данных (будем называть её контейнером блоков):
Имеется множество $$$s$$$ натуральных чисел, изначально пустое. Все числа в $$$s$$$ будут находиться на отрезке $$$[1, n]$$$ и гарантируется, что все операции корректны (например, число, уже содержащееся в $$$s$$$, не будут просить добавить туда). Контейнер блоков может:
- добавить число $$$x$$$ в $$$s$$$ за $$$\mathcal{O}(\log n)$$$;
- найти длину самого длинного отрезка последовательных чисел в $$$s$$$ за $$$\mathcal{O}(1)$$$;
- найти любой самый длинный отрезок последовательных чисел в $$$s$$$ и удалить его наименьший элемент из $$$s$$$ за $$$\mathcal{O}(\log n)$$$.
Если писать на C++, контейнер блоков можно реализовать, например, через два std::set'а, хранящих одно и то же — множество блоков (отрезков) из последовательных чисел в $$$s$$$ (числа, соседние с краями блока, должны быть не в $$$s$$$, например, при $$$s = {1, 5, 3, 2, 6}$$$, множество блоков будет $$${[1, 3], [5, 6]}$$$), первый std::set будет сортировать блоки по их левой границе, а второй — по длине.
- При добавлении числа $$$x$$$ в $$$s$$$, контейнер добавляет новый блок $$$[x, x]$$$, а потом, используя первый
std::set, находить, нужно ли объединить некоторые отрезки с новым (т.е. есть ли два блока, чьи границы — соседние числа), и, если есть, объединять их. Контейнер объединяет отрезки, удаляя старую пару отрезков из обоихstd::set'ов и добавляя объединённый отрезок обратно в каждый изstd::set'ов. - Для нахождения самого длинного отрезка, контейнер просто обращается ко второму
std::set'у. - Для удаления левого элемента найденного выше отрезка, контейнер удаляет отрезок из обоих
std::set'ов, увеличивает его левую границу на $$$1$$$, и, если отрезок всё ещё непустой, возвращает его обратно в каждый изstd::set'ов.
На других языках можно писать аналогично, если есть встроенный set, поддерживающий кастомную сортировку и нахождение наименьшего элемента, или, как альтернативная реализация, можно написать дерево отрезков на булевом массиве длины $$$n$$$, отражающем присутствие/отсутствие ($$$1$$$/$$$0$$$) чисел в $$$s$$$, хранящее в узле границы самого длинного подотрезка отрезка узла из последовательных единиц.
Теперь, покажем, как быстро находить нужные $$$k$$$. Будем хранить $$$S$$$ в контейнере блоков. Если длина какого-то блока в $$$S$$$ больше $$$a_i$$$ и равна $$$l + a_i$$$, $$$l$$$ наименьших элементов этого блока будут являться проигрышными силами для клетки $$$i$$$ — это определяет все проигрышные силы для клетки $$$i$$$ и только их. Тогда давайте просто убирать наименьший элемент самого длинного блока, пока его длина больше $$$a_i$$$ — очевидно, при этом все убранные числа будут являться всеми проигрышными силами для клетки $$$i$$$. Запросов к контейнеру блоков на нахождение и удаление при рассмотрении клетки $$$i$$$ тогда на $$$1$$$ больше, чем количество проигрышных позиций $$$(i, k)$$$, то есть суммарно по всем клеткам не более $$$\mathcal{O}(n \log n)$$$. Запросов к контейнеру блоков на добавление, как мы показывали раньше, тоже не более $$$\mathcal{O}(n \log n)$$$.
То есть для каждой клетки мы сначала обрабатываем пришедшие из очереди с приоритетом события, сгружаем новые элементы в $$$S$$$ контейнера блоков, потом находим проигрышные позиции и, наконец, добавляем новые события для следующих клеток в очередь с приоритетом. Получается, мы в процессе узнаем каждую проигрышную позицию, остаётся просто поставить проверку, проигрышная ли позиция $$$(1, 1)$$$.
Итого $$$\mathcal{O}(n \log^2 n)$$$ на запросы в очереди с приоритетом, $$$\mathcal{O}(n \log^2 n)$$$ на запросы к контейнеру блоков, суммарная асимптотика $$$\mathcal{O}(n \log^2 n)$$$.
#include <bits/stdc++.h>
#define int long long
using namespace std;
using pii = pair<int, int>;
struct Block {
int l, r;
Block() {}
Block(int l, int r) : l(l), r(r) {}
Block merge(const Block &o) const {
return Block(min(l, o.l), max(r, o.r));
}
};
auto cmp_len = [](const Block &a, const Block &b) {
if (a.r - a.l == b.r - b.l) return a.l < b.l;
return a.r - a.l > b.r - b.l;
};
auto cmp_x = [](const Block &a, const Block &b) {
if (a.l == b.l) return a.r < b.r;
return a.l < b.l;
};
struct BlockContainer {
set<Block, decltype(cmp_x)> sx{cmp_x};
set<Block, decltype(cmp_len)> slen{cmp_len};
void insert(int x) {
Block b(x, x);
auto r = sx.lower_bound(Block(x + 1, 0));
if (r != sx.end() && r->l == x + 1) {
b = b.merge(*r);
sx.erase(*r);
slen.erase(*r);
}
auto l = sx.lower_bound(Block(x - 1, 1e9));
if (l != sx.begin()) {
l--;
if (l->r == x - 1) {
b = b.merge(*l);
sx.erase(*l);
slen.erase(*l);
}
}
sx.insert(b);
slen.insert(b);
}
int get_max_length() {
Block b = *slen.begin();
return b.r - b.l + 1;
}
int pop_longest() {
Block b = *slen.begin();
slen.erase(b);
sx.erase(b);
int ans = b.l;
b.l++;
if (b.l <= b.r) {
slen.insert(b);
sx.insert(b);
}
return ans;
}
bool empty() {
return sx.empty();
}
};
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
priority_queue<pii> q; // <cell, num>
BlockContainer s;
for (int i = 1; i < n; i++) {
q.emplace(n - i - 1, i);
}
bool lose = 0;
for (int c = n - 1; c >= 0; c--) {
while (!q.empty() && q.top().first == c) {
int x = q.top().second;
s.insert(x);
q.pop();
}
while (!s.empty() && s.get_max_length() >= a[c] + 1) {
int x = s.pop_longest();
q.emplace(c - x - 1, x);
if (x == 1 && c == 0) lose = 1;
}
}
cout << (lose ? 2 : 1) << "\n";
}
signed main() {
ios_base::sync_with_stdio(0); cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}









A fun fact that CCO '26 P2 is similar to problem E :) In that question you are supposed to output a construction instead of finding the total possible permutations.
an O(n) solution for E: 377668463
The idea is that you can find the nearest smaller element to the left and right of each position using a monotonic stack. These determine the cartesian tree corresponding to all valid permutations
Thank you! Our testers also pointed out that there is an $$$\mathcal{O}(n)$$$ solution (that is one of the reasons for the decision of making the problem have the index E), and we are planning to add it to the tutorial soon.
I did the same, although implementation ended up easier maintaining only two arrays l and r of nearest bigger element to the left and to the right (without the stacks): 377671821
I took a different approach to E, filling in numbers top-down rather than from left/right ends. Idea is to start with a set of intervals $$$\left\lbrace[i,i] : a[i]=1\right\rbrace$$$ (which must be the local maxima) and trivial intervals between adjacent elements. Repeatedly consider the endpoints of the intervals (only needing to reconsider a point when it's the endpoint of a new interval). For each interval maintain the total number of possible orderings, and when we join two intervals together, the number of orderings of the new one is $$$\text{left_orderings}*\text{right_orderings}*\binom{\text{left_size}+\text{right_size}}{\text{left_size}}$$$; continue until we can't proceed or everything has been joined into one interval. I did this with DSU for $$$O(n \alpha(n))$$$ but it can straightforwardly be done in $$$O(n)$$$ using linked lists.
there is some writing mistake in E's solution :
let's check this condition for the indices [l,r] in the following order: l,r,l+1,r−1,r+2,r−2,… . We will prove that with this optimization, the algorithm will run in O(n^2) time.should be
let's check this condition for the indices [l,r] in the following order: l,r,l+1,r−1,l+2,r−2,… . We will prove that with this optimization, the algorithm will run in O(nlogn) time.Thanks, fixed now
what about in E->Solution->4.
does the combinatoric notation works as C[top:choose][bottom:total]
in my experience it's the opposite
Huh, today I learned from Wikipedia that "from $$$n$$$ choose $$$k$$$" is written as $$$C_n^k$$$ in Russian notation, but as $$$C_k^n$$$ in English notation. We will probably rewrite the English tutorial using the $$$\binom{n}{k}$$$ notation instead of $$$C$$$ then.
interesting
You missed to fixed the index. Instead of l+2 you wrote r+2.
Man htf can one come with the optimization done in E for a O(n logn) solution as proving this things is a different things but coming with them is not and this is the first time I am seeing such a optimization . Figured out the whole idea for E but didn't knew how to optimize it during the contest btw
Could someone elaborate complexity proof in E?
You can refer to my stream for proof here
You initially have $$$1$$$ segment of indices of size $$$n$$$, and every time you split a segment of size $$$x$$$ into segments of sizes $$$k$$$ and $$$x-k$$$, you do $$$O(k)$$$ work. Let's "charge" that work to the first $$$k$$$ elements of a segment. Then observe that every time you charge an element, the new segment it is in is at most size $$$\lfloor\frac{x}{2}\rfloor$$$. Therefore each element can be charged at most $$$log_2(n)$$$ times, which gives a total bound of $$$nlog_2(n)$$$.
Let's say the number of operations to solve subarray $$$(l, r)$$$ of size $$$s = r-l$$$ is $$$F(s)$$$. If we find the minimum in this subarray at index $$$i = l+k$$$, The naive solution takes $$$k$$$ steps to find $$$i$$$ then solve subarrays of size about $$$k$$$ and $$$s-k$$$ respectively. This gives the recurrence $$$F(s) = k + F(k) + F(s-k)$$$ for some $$$k \in [0, s]$$$.
The worst case is $$$F(k) = O(k^2)$$$ which happens when $$$k$$$ always equals $$$s$$$, corresponding to a decreasing permutation.
The optimized solution just loops from both sides at the same time, which reduces the number of steps to find the $$$i$$$ from $$$k$$$ to $$$\min(k, s-k)$$$. The new recurrence is $$$F(s) = \min(k, s-k) + F(k) + F(s-k)$$$.
Letting $$$a = \min(k, s-k)$$$, we get $$$F(s) \leq a + 2F(a)$$$ for some $$$a \in [0, \frac{s}{2}]$$$. Because the the function $$$a + 2F(a)$$$ is clearly increasing, the worst case will happen when $$$a$$$ always equals $$$\frac{s}{2}$$$.
Let $$$G(s) = \frac{F(s)}{s}$$$, dividing by s on both sides and plugging in the worst case,
I think there might be an issue with the step
followed by
Since
and
assuming F(x) is non-decreasing we have
Therefore
not
Could you clarify how this inequality is obtained, or if there is an additional argument that I'm missing?
you are correct I am sad now :(
$$$T(n)\le\alpha\min(k, n - k) + T(k) + T(n - k)$$$ for some $$$\alpha$$$.
Assume by induction $$$T(i) \le C\cdot n\log n$$$ for some $$$C$$$ and all $$$i \lt n$$$, then :
Now by symmetry assume $$$k \lt \frac n 2$$$, you get $$$\frac{n}{k}\ge2$$$ and since $$$\frac{n}{n-k}\ge 1$$$ :
So it suffices to take $$$C \ge \dfrac\alpha{\log 2}$$$ to conclude.
My detailed 3 hour 15 minute detailed video editorial is now available here.
Can someone point out why I keep gettting TLE in probleme E here 377706411 ?
I thought in a similar way to the editorial and I implemented the
O(nlog(n))way, with the tiny difference that I computed the(i−l+1)(r−i+1)by adding a number in every iteration, but I don't think it influences the execution time.Thanks in advance!
i think it is because for (int i=l; i<=mid; i++) { bc time of work is N/2 + (N-1)/2 + (N-2)/2 ... + 1 = O(N^2)
How to optimize it into
O(n(logn))? I don't understand the solutionyou need to first check the leftmost element of the segment, then then rightmost, then the second leftmost, then the second rightmost and so on
Can you please give the proof how this effect time complexity? Problem E.
For D one can do a braindead memoized recursion. We only need to notice that there are 3 types of numbers: $$$a$$$, $$$b$$$, $$$a \oplus b$$$. Let's call them types: 1, 2, 3.
Let the answer for the problem be $$$f(a, b, k)$$$. Let $$$g(x)$$$ be the product of the number of set bits and the number of zero bits. What is the transition?
$$$f(a, b, k) = f(a, a \oplus b, k - 1) + f(a \oplus b, b, k - 1) - g(a \oplus b)$$$.
That is, in the types notation we have:
$$$f(1, 2, k) = f(1, 3, k - 1) + f(3, 2, k - 1) - g(3)$$$
Now, note that $$$f(t_1, t_2, k') = f(t_2, t_1, k')$$$, so we can only store the answer for the triplets $$$(t_1, t_2, k')$$$ with $$$t_1 \lt t_2$$$. One can use
mapto conveniently store such states.For each $$$k' \le k$$$ we need to store at most $$$3$$$ states, so the total number of recursion calls is at most $$$3k$$$.
ouch that's what I did lmao
Can someone explain problem C? I don't mean the solution, just the problem, I don't understand it...
Imagine you have $$$n$$$ containers (cylindrical and with the same area at the bottom) arranged in a circle with adjacent containers being connected by a tube (the tube's connection is at the same height in both containers). When you add water to a container, the water level rises, but if it exceeds the level of one of the tubes, then the water will start flowing through the tube. That means that if you keep filling the tube, then eventually the water level of both containers will be the same (assuming that the water doesn't escape to another container). The formalization just says that when the water level of at least one of the containers is above the tube that connects them, then their water levels must be equal because otherwise water would flow from the container of higher volume to the one of lower volume.
The task is to find a way to add water to the containers so that no water overflows into the container $$$i$$$ while maximizing the total amount of water in all the containers (the actual task is to find the amount of water not the way to fill the containers).
Is this explanation clear or is there a doubt I didn't address?
Now I understand it, thank you so much
Good compitition! Short code length of DEF made me became Master lol.
In editorial for Problem C, what do you mean by i != l? What is l here?
there is a sparse table + binary search approach to F too . that felt more intuitive to me . we can rotate the array, to get the peak at the end . each particular value will contribute to some elements only(that too will be in a contiguous segment), then we can use difference array and finally get the answer..
code. used gemini for the code during upsolving , cuz i fumbled implementation.
for E you can notice that the range of the first element always should be $$$[1, a_1]$$$, then then from that, you can find the range of the $$$a_1 + 1$$$-th element with a division as the length should be $$$\frac{a_{a_1 + 1}}{{a_1 + 1}}$$$ and from that you find the range corresponding to the $$$\frac{a_{a_1 + 1}}{{a_1 + 1}} + a_1 + 1$$$-th element and so on..., basically you can find all of the right parents of element 1 until the root (the element with the range $$$[1, n]$$$), we then mark everyone, and then do the same process for of $$$2, 3, ... , n$$$ each time you repeat the process until you reach the root or a marked element (for $$$2$$$ for example you can find all right parent until you reach some range that starts with $$$1$$$ which has been marked before). to satisfy all conditions for each division the numerator should be divisible by the denominator and also you should never go out of range, also after that you should check that the ranges you found satisfy the condition: "any two segments either dont intersect at all or one is contained in the other", you can sort the ranges in $$$O(n)$$$ or $$$O(n \log n)$$$.
$$$O(n)$$$ submission: 377742663
In Solution 2 of problem D, $$$F(l,A,B)=F(l−1,A,A⊕B)+F(l−1,A⊕B,B)$$$+[product of the $$$0$$$ and $$$1$$$ bit counts for $$$A⊕B$$$] should be replaced by $$$F(l,A,B)=F(l−1,A,A⊕B)+F(l−1,A⊕B,B)$$$ $$$\bf{-}$$$ [product of the $$$0$$$ and $$$1$$$ bit counts for $$$A⊕B$$$ ]. I think, it was typo. Also, the time complexity given is wrong I guess because for each $$$k$$$ we can have at max $$$6$$$ permutations of $$$A, B, C$$$ so, $$$6k$$$ states total. And, we need $$$O(\log k)$$$ time to calculate each state. Hence, time complexity should be $$$O(n + k\log k)$$$
Thank you, it is now fixed
Guys, i wonder if the technique used in problem E can be applied to other dnc like technique? I meant like what if instead of a permutation or an array, we are given the permutation of tree nodes? Can the same technique apply?
The formulation for problem C is too vague imo. On a second read this problem is very doable, but essentially it should be very intuitive.
It is not immediately readable how the sequences
h_iandw_irelate. In particular,h_irepresents the height of the connection between vesseliand vessel(i mod n) + 1(so, the next vessel in the circle), but this is not emphasized clearly enough. The term “partition” in the input description also feels a bit vague. “Connection” or something similar would fit better here.This causes the formal definition to feel difficult, when it should be trivial to understand. A better formulation for this would imo be:
Between vessel
iand vessel(i mod n) + 1, there is a connection at heighth_i. If the water level in either of the two vessels rises strictly above this connection height, then the two vessels must have equal water levels.l, we need to output the maximum possible value ofw_1 + w_2 + ... + w_n, over all good arrays satisfyingw_l = 0.So, for the plebs like me solving problem C:
You have a circle of water tanks, and they are connected to their neighbours with tubes. The heights of those tubes are given by the array
h, whereh_iis the height of the connection between vesseliand vessel(i mod n) + 1. Soh_1connects vessels1and2,h_2connects vessels2and3, ..., andh_nconnects vesselnback to vessel1, completing the circle.The array
wdescribes the water level in each vessel. The condition says: if the water level in either of two neighboring vessels is strictly above the height of the tube between them, then those two vessels must have the same water level.For each vessel
l, letG_lbe the set of all good arrayswsatisfyingw_l = 0, meaning vessellremains empty.For each
G_l, we want the maximum possible total amount of water across all vessels. Formally, this is the maximum possible value ofw_1 + w_2 + ... + w_n, over all arrays inG_l. Let this maximum value be denoted bym_l.Your goal is to output the sequence:
m_1 m_2 m_3 ... m_nNote Consider the first test case, where
n = 4andh = [1, 2, 3, 4].To keep vessel
1empty, the arrayw = [0, 0, 1, 0]is a good array inG_1, because no pair of neighboring vessels has a water level strictly above the height of the connection between them. Its total volume is1.However, this array is clearly not maximal. A maximal array in
G_1isw = [0, 1, 2, 3], with a total volume of6. Therefore,m_1 = 6.So
w = [0, 1, 2, 3]is a good array with sum6, and it can be shown that no array inG_1has a larger sum.Similarly:
2empty, one maximal good array isw = [1, 0, 2, 3], thus with am_2of6;3empty, one maximal good array isw = [2, 2, 0, 3], thus with am_3of7;4empty, one maximal good array isw = [3, 3, 3, 0], thus with am_4of9.Therefore, the final output is constructed from the indexed maximum good values
m_1 m_2 m_3 m_4:6 6 7 9Problem C had a very confusing explanation, that could use some work. The problem itself wasn't bad tho
Obviously the only numbers we will encounter are the first number $$$A$$$, the second number $$$B$$$, and their XOR $$$C$$$. Let’s simulate a few steps (it’s not hard to quickly write a script). For each step, let’s track the amount of $$$A$$$, $$$B$$$, and $$$C$$$ added, but let’s track $$$A + B$$$ because they’re symmetric:
It’s clear that the two changes share the same pattern, only different starting points. Let’s put it into OEIS!!! :money_mouth:
We find A078008 which has the following recurrence relation:
With the exception of the first step, the amount of $A$ and $$$B$$$ added on the $$$n$$$-th step is equal to $$$\frac{A_{n-1}}{2}$$$ and the amount of $$$C$$$ is equal to $$$A_{n-2}$$$. The rest is trivial, if you really need it check my AC submission.
Induction proof for $$$n \log n$$$ in E:
We spend $$$ck$$$ time iterating through the array to then recurse on problem sizes $$$n-k$$$, $$$k$$$. (I'm going to avoid big-O as much as possible because you can make errors with it easily because you might absorb constants too much and actually end up with an extra log or even exponential factor.) Suppose that by induction we can solve the problem size $$$k$$$ in $$$C k \log_2 k$$$ and $$$n-k$$$ in $$$C(n-k) \log_2 (n-k)$$$.
Then
$$$T(n) = T(n-k) + T(k) + ck,$$$
$$$T(n) \le C(n-k) \log_2(n) + Ck \log_2(k) + ck.$$$
We know that $$$k \le \frac{n}{2}$$$, so $$$\log_2 k$$$ is at most $$$\log_2(n) - 1$$$:
$$$T(n) \le C(n-k) \log_2(n) + Ck (\log_2(n) - 1) + ck,$$$
$$$T(n) \le Cn \log_2 n - Ck + ck.$$$
Now we see that $$$T(n) \le Cn \log_2 n$$$, as desired, if $$$C \ge c$$$.
I solved D without the observation that all numbers must be A, B or A^B.
For a fixed bitset t, #1 * #0 = $$$\sum_{i=1}^n\sum_{j=i+1}^nt[i] \oplus t[j]$$$.
Answer will be $$$\sum_{l=1}^{2^k+1}\sum_{i=1}^n\sum_{j=i+1}^na[l][i] \oplus a[l][j]=\sum_{i=1}^n\sum_{j=i+1}^n\sum_{l=1}^{2^k+1}a[l][i] \oplus a[l][j]$$$. $$$a[l][i]$$$ and $$$a[l][j]$$$ are dependent only on $$$a[1][i], a[2^k+1][i], a[1][j], a[2^k+1][j]$$$. There are only 16 combinations of values of $$$a[1][i], a[2^k+1][i], a[1][j], a[2^k+1][j]$$$, we can group (i,j) according to these values and calculate the answer. For a fixed quadruple we use dp given in the second solution, but each l will have 16 states at most as both A <= 3 and B <= 3 and we can do it explicitly.
E is beautiful, that optimization technique to show that search cost changes from O(n)->O(k) and now depends on smaller child which keeps halving because of the way we are iterating is very clever.
I actually solved it slightly differently by deducing the boundaries in $$$O(N)$$$ time.
First, the core observation for the formula: if an element at index $$$i$$$ is the minimum in the exclusive range $$$(l, r)$$$, it is the minimum for exactly $$$(i - l)$$$ valid left endpoints and $$$(r - i)$$$ valid right endpoints. Thus, $$$p[i] = (i - l) \times (r - i)$$$.Instead of searching for a valid root inside a known boundary, we can logically deduce the exact $$$(l, r)$$$ bounds for every element from left to right:Assume the array is padded with $$$-\infty$$$ at both ends. For index $$$0$$$, its previous smaller element is trivially $$$l = -1$$$.Since we know $$$p[0]$$$ and $$$l = -1$$$, we can directly calculate its next smaller element (NSE): $$$r = 0 + \frac{p[0]}{0 - (-1)}$$$.
This index $$$r$$$ gives us two things:Index $$$0$$$ is the absolute minimum of the subarray $$$[1, r-1]$$$ as its Next Smaller element was at $$$p[0]$$$ all the elements in b/w those elements were bigger than $$$0th$$$ element. We can recursively find the bounds for the inner elements $$$[1, r-1]$$$ as the bounds of this subarray is 0 and r which are obviously bigger then all the elements in the subarray giving us effectively $$$-\infty$$$ outer bounds. Now the element at $$$r$$$ is smaller than the element at $$$0$$$, the left boundary of r'th index is also $$$-1$$$ .We repeat the process for $$$r$$$. We use this to find its NSE, $$$r_1$$$, and recursively process the inner subarray $$$[r+1, r_1-1]$$$.By chaining this forward, we calculate the exact $$$[l, r]$$$ bounding box for every single element in $$$O(N)$$$ time.Now we mapped out the exact $$$(l, r)$$$ for every element, we can just build a reverse map: $$$ \lt l, r \gt $$$ -> index.To solve the subsegment $$$(L, R)$$$ finding the smallest index, we just query our reverse map.The key should be present exactly once in the subarray.If it does, let $$$i$$$ be the mapped index. We split the problem into $$$[L, i-1]$$$ and $$$[i+1, R]$$$, and multiply their results by $$$\binom{R - L - 2}{i - L - 1}$$$.
I also had a simillar solution to E (378239703), with the difference that ater calcualting (l,r), I created a tree of non-equalities —
uis a parent ofvif $$$u \lt v$$$ and there is no nodexsuch that $$$u \lt x \lt v$$$. After that I do a dp on the tree wheredp[u]=# of possible ways to arrange the subtree of u, where $$$dp[leaf]=1$$$ anddp[u] = MERGE all children of uwhere $$$MERGE(u,v)=dp[u]*dp[v]*(^{SIZ(u)+SIZ(v)}_{SIZ(u)})$$$. Computing $$$(^n_k)$$$ is done inO(log n), so my solution isO(n log n).My question is how do you avoid that
log npart in your solution?that's actually so good and easier to understand than author solution
technique of E is great, but i have no idea how to come up with something like that during the contest and not just believe but proof that
its classic dnc. patterns like this become very obvious over practice.
I think it comes with some practice with doing runtime analysis for divide and conquer algorithms (like doing recurrence relations algebraically and also recursion trees), and also just a bit of Russianness. Like in contest I was like "oh brute force search would work but it would be n^2 if the value was near the edges because the recursion tree would be too deep" -> "wait what if I just check the edges first" -> prove the complexity.
thanks! My main problem here is prove I think, I will practice it more. And I already have a lot of Russianness :)
I observed a interesting observation in D let $$$m = (2^k)+1$$$ The frequency value $$$A_1$$$ and $$$A_n$$$ and $$$Xor(A_1,A_n)$$$ will be $$$Ceil(m/3)$$$, $$$Ceil(m/3)$$$, $$$floor(m/3)$$$. so by this observation you can do it in $$$O(n)$$$ time complexity
in problem E, isn't it sufficient to find range maximum element on each range and check if it is really the count of subarrays?
Not always. Consider $$$n = 1001$$$, a min at the left or right edge would have 1001 arrays to be the min, but a min at the middle would have $$$501 \cdot 501 = 251{,}001$$$ arrays to be the min.
D is not 1500 are u kidding me :(
Actually C was worth 1400 D is 1100 or 1000 at max dude
How can someone think of the optimisation done in E it stills feels like n^2 to me
For D, can anyone explain how the solution is
O(n)? or any resources where I can understand the concept.Try to come up with a recursive solution that works in (n2) How n2 will pass here?
The python solution for problem C meets TLE. The same algorithm implemented in C/C++ is accepted.
In the problem C, the statement was very confusing. And very hard to visualize..