Всем спасибо за участие! Надеемся, вам понравились задачи.
2176A - Операции с инверсиями
Какой элемент массива нельзя удалить при помощи описанных операций?
При каком условии нельзя удалить элемент $$$a_j$$$?
Обратим внимание, что элемент $$$a_1$$$ нельзя удалить с помощью описанных операций.
В общем случае $$$j$$$-й элемент нельзя удалить, если $$$a_j$$$ не меньше, чем все элементы $$$a_1, \ldots, a_{j - 1}$$$.
В ином случае среди элементов $$$a_1 \ldots a_{j - 1}$$$ всегда найдётся элемент $$$a_i \gt a_j$$$, а значит $$$a_j$$$ можно будет удалить.
Ограничения достаточно маленькие, поэтому для каждого элемента можно проверить данное утверждение за $$$O(n)$$$ на одну проверку — всего получается $$$O(n^2)$$$ на тест.
Идея решения за $$$O(n)$$$ целиком следующая:
- Будем обрабатывать элементы, начиная с $$$a_2$$$, до $$$a_n$$$
- Обозначим через $$$M$$$ наибольший среди уже обработанных элементов, изначально $$$M = a_1$$$
- Если $$$M \gt a_j$$$, то $$$a_j$$$ удаляется; иначе установим $$$M$$$ равным $$$a_j$$$
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <ctime>
#include <queue>
#include <iomanip>
#include "assert.h"
#include <math.h>
#include <set>
#include <deque>
using namespace std;
#define vi vector<int>
#define vii vector<pair<int, int>>
#define pii pair<int, int>
#define ll long long
#define pll pair<ll, ll>
const ll INF = 2e18;
void solve() {
int n;
cin >> n;
vi a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int cmx = 0;
int mx = 0;
for (int i = 0; i < n; i++) {
mx = max(mx, a[i]);
if (a[i] == mx) cmx++;
}
int res = n - cmx;
cout << res << "\n";
}
int main() {
srand(time(0));
ios::sync_with_stdio(0);
cout.tie(0), cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
2176B - Оптимальные сдвиги
Какой ответ для строк "101", "1010", "10101", "101010" и так далее?
Различается ли оптимальный набор операций для строк "100", "010" и "001"?
Какой ответ для строк "100", "0100", "00100", "001000" и так далее?
Различается ли ответ для строк "0100100", "100011", "001110110"?
Рассмотрим все группы подряд идущих нулей в зацикленной строке $$$S$$$.
Пусть количества нулей в данных группах равны $$$z_1, z_2, \ldots, z_k$$$, где $$$k$$$ — количество групп.
В таком случае ответ равен $$$MZ = \max(z_j)$$$.
Покажем, что ответ $$$MZ$$$ всегда достижим как ответ:
- $$$MZ$$$ раз выполним операцию с $$$d = 1$$$.
- После каждой операции все $$$z_j \gt 0$$$ будут уменьшаться на $$$1$$$, так как самый левый ноль в каждом блоке будет превращаться в единицу.
- Так как $$$MZ = \max(z_j)$$$, то после ровно $$$MZ$$$ операций все $$$z_j$$$ будут равны нулю.
Покажем, что нельзя получить ответ лучше, чем $$$MZ$$$:
- Предположим, что существует ответ с суммой значений циклических сдвигов $$$S = d_1 + d_2 + \dots + d_m$$$, где $$$S \lt MZ$$$.
- Рассмотрим наибольший блок из нулей (его размер равен $$$MZ$$$) и единицу слева от этого блока.
- При применении первой операции со сдвигом $$$d_1$$$ у нас не могло появиться единицы на позиции правее $$$d_1$$$ в этом блоке.
- При применении следующих циклических сдвигов данная единица также может переместиться правее только на значение сдвигов.
- Суммарно данная единица сможет переместиться только на $$$S$$$ позиций вправо от изначального положения, что строго меньше $$$MZ$$$ — таким образом последний ноль блока останется нулём, что противоречит исходному предположению.
#include <iostream>
#include <vector>
using namespace std;
#define vi vector<int>
#define ll long long
void solve() {
int n;
cin >> n;
string s;
cin >> s;
s += s;
n *= 2;
int cur = 0;
int res = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '1') cur = 0;
else cur++;
res = max(res, cur);
}
cout << res << "\n";
}
int main() {
int T;
cin >> T;
while (T--) {
solve();
}
}
2176C - Нече(с)тный процесс
У вас есть $$$n$$$ монет, причём все они чётные. Какие будут ответы для всех $$$k$$$?
У вас есть две монеты: чётная и нечётная. Какие будут ответы для $$$k = 1$$$ и $$$k = 2$$$?
У вас есть $$$n$$$ монет, причём ровно одна из них нечётная. Какие будут ответы для всех $$$k$$$?
У вас есть $$$n$$$ монет, причём все они нечётные. Какие будут ответы для всех $$$k$$$?
У вас есть $$$n$$$ монет, причём ровно три из них нечётные. Чему будут равны ответы для $$$k = (n - 2)$$$, $$$(n - 1)$$$ и $$$n$$$ соответственно?
У вас есть $$$n$$$ монет, причём ровно четыре из них нечётные. Чему будут равны ответы для $$$k$$$ от $$$(n - 3)$$$ до $$$n$$$?
Если все имеющиеся монеты чётные, то независимо от ваших действий мешок будет пустым каждый раз.
Пусть у нас есть одна нечётная монета $$$oc$$$ и несколько чётных $$$ec_1, ec_2, \dots, ec_m$$$.
Отсортируем чётные монеты в порядке убывания: $$$ec_1 \ge ec_2 \ge \dots ec_m$$$.
В таком случае для $$$k = 1 \dots (m + 1)$$$ мы можем построить следующие ответы:
- $$$oc$$$
- $$$oc + ec_1$$$
- $$$oc + ec_1 + ec_2$$$
- $$$\dots$$$
- $$$oc + ec_1 + ec_2 + \dots + ec_m$$$.
Но что делать, если нечётных монет несколько?
Пусть $$$oc$$$ — наибольшая из нечётных монет.
Проделаем для всех $$$k$$$ от $$$1$$$ до $$$(m + 1)$$$ описанные выше действия.
Для $$$k = (m + 2)$$$ используем следующую конструкцию:
- Вначале положим в мешок любые две нечётные монеты, не являющиеся монетой $$$oc$$$.
- Далее положим монету $$$oc$$$ и все чётные монеты, за исключением наименьшей $$$ec_m$$$.
Для $$$k = (m + 3)$$$ добавим к уже построенному ответу наименьшую чётную монету $$$ec_m$$$.
Для $$$k = (m + 4)$$$ добавим ещё две "ненужные" нечётные монеты в начало и опять уберём $$$ec_m$$$.
Для $$$k = (m + 5)$$$ опять добавим $$$ec_m$$$ и так далее.
Обратите внимание на $$$k = n$$$: если всего у вас имеется чётное количество нечётных монет, то независимо от порядка добавления мешок будет опустошён в конце.
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <ctime>
#include <queue>
#include <iomanip>
#include "assert.h"
#include <math.h>
#include <set>
#include <deque>
using namespace std;
#define vi vector<int>
#define vii vector<pair<int, int>>
#define pii pair<int, int>
#define ll long long
#define pll pair<ll, ll>
const ll INF = 2e18;
void solve() {
int n;
cin >> n;
vi a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vi odd, even;
for (int i = 0; i < n; i++) {
if (a[i] & 1) odd.push_back(a[i]);
else even.push_back(a[i]);
}
sort(odd.begin(), odd.end());
sort(even.begin(), even.end());
reverse(even.begin(), even.end());
vector<ll> prefeven((int)even.size() + 1);
if (even.size() > 0) {
prefeven[0] = 0;
for (int i = 0; i < even.size(); i++) prefeven[i + 1] = prefeven[i] + even[i];
}
ll ans = -INF;
int cntodd = (int)odd.size();
int cnteven = 0;
for (int i = 0; i < n; i++) {
if (a[i] % 2 == 0) cnteven++;
}
int odd1 = 1, even1 = 0;
if (odd.size() == 0) odd1 = 0, even1 = 1;
for (int k = 1; k <= n; k++) {
if (k > 1) {
if (even1 < even.size()) even1++;
else {
if (odd1 + 2 <= odd.size() && even1 > 0) {
odd1 += 2;
even1--;
}
else {
odd1++;
}
}
}
if (odd1 & 1) {
cout << odd.back() + prefeven[even1] << " ";
}
else {
cout << 0 << " ";
}
}
cout << '\n';
}
int main() {
srand(time(0));
ios::sync_with_stdio(0);
cout.tie(0), cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
2176D - Пути Фибоначчи
В рамках этого разбора мы будем называть простые пути графа, последовательность чисел на которых образует обобщённую последовательность Фибоначчи — путями Фибоначчи.
Подумайте о том, как выглядят самые простые пути Фибоначчи.
Самые простые пути Фибоначчи — это пути из двух вершин ($$$u,v$$$), соединённых ребром. Более того, любое ребро графа является путём Фибоначчи, так как соответствующая ребру последовательность чисел (концы ребра) состоит всего из двух чисел.
Теперь подумайте, как выглядят более длинные пути Фибоначчи.
Более длинные пути Фибоначчи состоят из последовательности рёбер, таких что числа на концах первого ребра — произвольные. Но число на конце любого другого ребра — это сумма чисел на конце предыдущего ребра и начале текущего ребра.

Подумайте о динамическом программировании на рёбрах.
Давайте посчитаем $$$dp[uv]$$$ — количество путей Фибоначчи, которые начинаются с ребра ($$$u,v$$$). Переходы в такой динамике будут выглядеть как $$$dp[uv]=\sum dp[vw]$$$, по таким рёбрам ($$$v,w$$$) для которых выполняется $$$cost[w]=cost[u]+cost[v]$$$.
Как посчитать такую динамику, если она зависит от порядка обработки рёбер? Мы могли бы ввести параметр $$$k$$$ — зафиксировать длину пути Фибоначчи, и тогда динамика выглядела бы как $$$dp[uv][k]=\sum dp[vw][k-1]$$$. Но есть более простой способ.
Заметим, что в пути Фибоначчи у каждого следующего ребра сумма чисел на его концах строго больше, чем сумма чисел на концах предыдущего ребра. Это означает, что рёбра с большей суммой чисел выгодно обработать строго раньше, чем рёбра с меньшей суммой чисел, а порядок обработки рёбер с одинаковой суммой может быть произвольным, так как они не могут входить в один путь Фибоначчи.
Тогда мы отсортируем рёбра, и останется только быстро пересчитывать сумму $$$dp[uv]=\sum dp[vw]$$$ из динамики. Наивно искать рёбра ($$$v, w$$$), у которых $$$cost[w]=cost[u]+cost[v]$$$ не получится, асимптотика такого решения легко будет $$$O(E^2)$$$ или хуже.
Поэтому сожмём рёбра с одинаковым значением на конце и идущие из одной вершины вместе. Для каждой вершины $$$u$$$ заведём словарь $$$dp[u][cost]$$$ — число путей Фибоначчи из вершины $$$u$$$, которые идут в какую-то вершину $$$v$$$ со значением $$$cost$$$.
Пересчет этой динамики такой $$$dp[u][cost[v]] = dp[u][cost[v]] + (1 + dp[v][cost[u] + cost[v]])$$$ — к значению динамики $$$dp[u][cost[v]]$$$ прибавляется число путей из вершины $$$v$$$ со стоимостью $$$cost[u]+cost[v]$$$, и ещё плюс единица за путь из одного ребра ($$$u,v$$$).
Асимптотика решения $$$O(E\cdot\operatorname{log}(E))$$$.
#include <bits/stdc++.h>
using namespace std;
#define vi vector<int>
#define vii vector<pair<int, int>>
#define pii pair<int, int>
#define ll long long
#define pll pair<ll, ll>
const ll INF = 2e18;
const int MOD = 998244353;
struct Edge {
int u, v;
ll s;
Edge(){}
Edge(int u, int v, ll s) : u(u), v(v), s(s){}
bool operator < (const Edge &other) const {
return s < other.s;
}
};
void add(int &a, int b) {
a += b;
if (a >= MOD) a -= MOD;
}
void solve() {
int n, m;
cin >> n >> m;
vector <ll> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vector <vi> g(n);
vector <Edge> edges;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
u--;
v--;
g[u].push_back(v);
edges.push_back(Edge(u, v, a[u] + a[v]));
}
sort(edges.begin(), edges.end());
reverse(edges.begin(), edges.end());
vector<map <ll, int>> sumdp(n);
int ans = 0;
for (auto e : edges) {
int u = e.u;
int v = e.v;
ll s = e.s;
//cout << u << " " << v << " " << s << endl;
int curdp = sumdp[v][s];
add(curdp, 1);
add(sumdp[u][a[v]], curdp);
add(ans, curdp);
}
cout << ans << "\n";
}
int main() {
srand(time(0));
ios::sync_with_stdio(0);
cout.tie(0), cin.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
}
2176E - Удалите за наименьшую стоимость
Придумайте решение для задачи без запросов.
Посмотрим на максимальные элементы. Что вы можете сказать про отрезки между ними?
Давайте вначале дадим теоретическую оценку ответа, затем алгоритм, который достигает этой оценки.
Введем $$$\operatorname{f}(l, r, x)$$$, которая означает следующее: пускай мы рассматриваем только элементы $$$a_l, a_{l + 1}, \ldots, a_{r - 1}$$$ и мы можем каждый из них вычеркнуть при помощи элемента, который имеет стоимость удаления $$$x$$$. Тогда какая минимальная стоимость удаления всего данного отрезка? Будем считать, что мы передаем в $$$x$$$ самое лучшее подходящее значение.
Тогда пусть $$$p_1, p_2, \ldots, p_m$$$ — это позиции всех максимумов на отрезке. Заметим следующие факты:
Для любых $$$i, j$$$, для которых существует $$$k$$$ такой, что $$$l \leq i \lt p_k \lt j \lt r$$$, не существует последовательности операций, при которой один из этих элементов удаляет другой
Хотя бы один из элементов $$$p$$$ обязан быть удален элементом вне этого отрезка
Элементы $$$p_1, p_2, \ldots, p_m$$$ могут быть удалены либо за стоимость $$$x$$$, либо за стоимость другого максимального элемента
Определим $$$x' = min(x, c_{p_1}, c_{p_2}, \ldots, c_{_m})$$$. Тогда из этих трех замечаний можно сделать вывод, что при оптимальном выборе $$$x$$$ ранее, $$$\operatorname{f}(l, r, x) = x' \cdot m + \operatorname{f}(l, p_1, x') + \operatorname{f}(p_1 + 1, p_2, x') + \ldots + \operatorname{f}(p_m + 1, r, x')$$$.
Это так, ведь по пункту 2 должен быть хотя бы один элемент, удаляемый элементом вне отрезка. По пункту 3 мы оставшиеся $$$m - 1$$$ максимумов можем удалить только при помощи $$$x$$$ и других элементов $$$p_i$$$, а оставшийся максимум — за $$$x'$$$(по условию). А по пункту 1 все отрезки между собой независимые, если не учитывать максимумы.
И в этом случае ответом на задачу будет $$$\operatorname{f}(0, n, inf) - inf$$$.
Теперь покажем, что такая последовательность удалений действительно существует.
Будем также рекурсивно удалять отрезки, но с условием, что этот минимальный элемент вне отрезка будет лежать строго слева либо справа от отрезка $$$l, r$$$. Тогда, когда мы выделили $$$x'$$$, мы можем делать следующий процесс:
У нас есть множество отрезков $$$(l_1, r_1), \ldots, (l_{m + 1}, r_{m + 1})$$$. У нас найдется отрезок, который является соседом элемента $$$x'$$$, рекурсивно удалим его. После этого у этого оптимального соседа будет соседом один из максимальных элементов (либо же мы удалили весь отрезок). Мы его удаляем за $$$x'$$$.
Этот процесс закончится, когда останется ровно один элемент, который можно удалить за $$$x'$$$. И несложно заметить, что мы достигли нашей оценки, а значит, рекурсивный алгоритм выше действительно возвращает ответ на задачу.
Что представляет из себя структура рекурсивных вызовов функции $$$\operatorname{f}$$$? Можем ли мы запомнить её, а затем по ней эффективно обрабатывать запросы? Для каких элементов изменится стоимость, за которую мы их удаляем, при обнулении некоторого $$$c_i$$$?
Заметим, что структура вызовов функции $$$\operatorname{f}$$$ представляет собой дерево. Давайте сохраним его и запомним, за какую цену мы удаляли каждый элемент, пусть это $$$r_i$$$.
Затем, когда приходит запрос обнуления $$$c_{p_i}$$$, возьмем узел, для которого $$$p_i$$$ является одним из максимумов, и запустим поиск в глубину по поддереву этой вершины, не заходя в вершины, для которых уже было выполнено обнуление. Таким образом, поменяем соответствующие значения $$$r_i$$$ на $$$0$$$ и пересчитаем глобальный ответ.
Сложность решения: $$$O(n \log{n})$$$, так как для реализации функции $$$\operatorname{f}$$$ нам нужно вычислять максимум на отрезке, для чего может понадобиться дерево отрезков или sparce table.
Возможны и другие подходы по задаче.
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <deque>
#include <queue>
#include <iomanip>
using namespace std;
#define ll long long
#define vi vector<int>
#define pii pair<int, int>
#define vii vector<pii>
const int N = 505010;
const int MOD = 1e9 + 7;
vi g[3 * N];
int cost1[3 * N], cost2[3 * N];
int tree[4 * N], pos[4 * N], a[N], c[N];
int nxtL[N], nxtR[N];
int num[N];
int n, q;
void build(int v, int tl, int tr) {
if (tl == tr) {
tree[v] = a[tl];
pos[v] = tl;
return;
}
int tm = (tl + tr) / 2;
build(v * 2, tl, tm);
build(v * 2 + 1, tm + 1, tr);
if (tree[v * 2] > tree[v * 2 + 1]) {
tree[v] = tree[v * 2];
pos[v] = pos[v * 2];
}
else {
tree[v] = tree[v * 2 + 1];
pos[v] = pos[v * 2 + 1];
}
}
pii getmax(int v, int tl, int tr, int l, int r) {
if (l > r) return { -1, -1 };
if (l == tl && r == tr) {
return { tree[v], pos[v] };
}
int tm = (tl + tr) / 2;
pii tmp = max(getmax(v * 2, tl, tm, l, min(r, tm)), getmax(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r));
return tmp;
}
int state = -1;
ll ans;
void addEdge(int v, int u) {
if (v == -1 || u == -1) return;
g[v].push_back(u);
}
int it = 0;
int precalc(int l, int r, int minc) {
int xx = minc;
state++;
int vres = state;
if (l > r) return vres;
pii p = getmax(1, 0, n - 1, l, r);
vi t;
int pos = p.second;
while (pos >= l) {
t.push_back(pos);
pos = nxtL[pos];
}
reverse(t.begin(), t.end());
pos = nxtR[p.second];
while (pos <= r) {
t.push_back(pos);
pos = nxtR[pos];
}
for (int x : t) minc = min(minc, c[x]);
for (int x : t) num[x] = vres;
if (xx <= (int)1e9) ans += minc;
cost1[vres] = minc;
ans += 1ll * minc * ((int)t.size() - 1);
addEdge(vres, precalc(l, t[0] - 1, minc));
for (int i = 1; i < t.size(); i++) {
addEdge(vres, precalc(t[i - 1] + 1, t[i] - 1, minc));
}
addEdge(vres, precalc(t.back() + 1, r, minc));
for (int v : t) num[v] = vres;
return vres;
}
void go(int v) {
if (!cost1[v]) return;
if (!g[v].size()) return;
if (v == 1) ans -= 1ll * ((int)g[v].size() - 2) * cost1[v];
else ans -= 1ll * ((int)g[v].size() - 1) * cost1[v];
cost1[v] = 0;
for (int u : g[v]) {
go(u);
}
}
void solve() {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> c[i];
vi p(n);
for (int i = 0; i < n; i++) cin >> p[i], p[i]--;
map <int, int> lst;
for (int i = 0; i < n; i++) {
if (lst.find(a[i]) == lst.end()) nxtL[i] = -1;
else nxtL[i] = lst[a[i]];
lst[a[i]] = i;
}
lst.clear();
for (int i = n - 1; i >= 0; i--) {
if (lst.find(a[i]) == lst.end()) nxtR[i] = n;
else nxtR[i] = lst[a[i]];
lst[a[i]] = i;
}
build(1, 0, n - 1);
for (int i = 0; i <= 3 * n; i++) g[i].clear();
ans = 0;
state = 0;
int root = precalc(0, n - 1, (int)1e9 + 1);
cout << ans << " ";
for (int i = 0; i < n; i++) {
int j = num[p[i]];
go(j);
cout << ans << " ";
}
cout << "\n";
}
int main()
{
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
2176F - оМега числа
Попробуйте выразить $$$\omega(x \cdot y)$$$ через $$$\omega(x)$$$ и $$$\omega(y)$$$.
$$$\omega(x \cdot y) = \omega(x) + \omega(y) - \omega(\operatorname{gcd}(x,y))$$$. Насколько большими могут быть $$$\omega(x)$$$ и $$$\omega(x \cdot y)$$$?
$$$\omega(x) \leq 6$$$, $$$\omega(x \cdot y) \leq 12$$$. Как посчитать число пар ($$$x,y$$$) с фиксированной суммой $$$\omega(x)+\omega(y)=const$$$ и фиксированным $$$\operatorname{gcd}(x,y)=const$$$?
Обозначим за $$$K$$$ наибольшее количество уникальных простых в разложении любого числа в заданных ограничениях. В этой задаче $$$K \leq 6$$$, и асимптотически $$$K=O(\operatorname{log}(maxA))$$$.
Давайте попробуем посчитать $$$dp[g][sum_{len}]$$$ — количество упорядоченных пар ($$$i,j$$$) таких, что $$$\operatorname{gcd}(a_i,a_j)=g$$$, а $$$\omega(a_i)+\omega(a_j)=sum_{len}$$$. Если мы умеем считать эту динамику, то ответ на задачу — это $$$\sum_{g=1}^{g=n}\sum_{len=1}^{len=2 \cdot K}dp[g][sum_{len}] \cdot (sum_{len}-\omega(g))^k$$$.
Довольно известным способом можно посчитать количество пар чисел массива ($$$i,j$$$), у которых $$$\operatorname{gcd}(a_i,a_j)=g$$$ для любого числа $$$g$$$ от $$$1$$$ до $$$maxA$$$. Давайте модифицируем этот способ для нашей задачи.
Пусть $$$cnt[x][len]$$$ — количество чисел исходного массива, которые делятся на $$$x$$$, и $$$\omega(x)$$$ для которых равен $$$len$$$.
Чтобы посчитать эту вспомогательную динамику, мы можем проитерироваться по каждому $$$x$$$ от $$$1$$$ до $$$maxA$$$, для каждого $$$x$$$ проитерироваться по его кратным и прибавить к $$$cnt[x][\omega(i \cdot x)]$$$ количество кратных $$$i \cdot x$$$ из массива. Асимптотика этого подсчета $$$O(n \cdot \operatorname{log}(n))$$$ (частичные суммы гармонического ряда).
Теперь мы готовы посчитать динамику $$$dp[g][sum_{len}]$$$. Давайте проитерируемся по наибольшему общему делителю $$$g$$$ от $$$maxA$$$ до $$$1$$$. Если бы мы просто хотели посчитать число пар с $$$\operatorname{gcd}=g$$$, то мы бы вычли уже посчитанные пары с большим кратным $$$\operatorname{gcd}$$$.
Но теперь у нас еще фиксирована суммарная длина $$$\omega(x)$$$ обоих чисел пары. Поэтому при фиксированном $$$g$$$ переберем еще 2 параметра — $$$len_a$$$ и $$$len_b$$$, то есть $$$\omega(a)$$$ и $$$\omega(b)$$$ для обоих чисел.
Тогда $$$dp[g][sum_{len}]=dp[g][\omega(a) + \omega(b)]=dp[g][i+j]=cnt[g][i] \cdot cnt[g][j]$$$, где $$$i,j$$$ перебираются от $$$1$$$ до $$$K$$$. Здесь также надо быть аккуратным и учесть случай, когда $$$i=j$$$, в таком случае к $$$dp[g][i+j]$$$ прибавляется $$$\frac{cnt[g][i] \cdot (cnt[g][i] - 1)}{2}$$$.
Далее мы просто вложенным циклом перебираем кратные $$$g$$$: $$$2 \cdot g, 3 \cdot g, \ldots$$$, чтобы вычесть пересечения $$$dp[g][sum_{len}] = dp[g][sum_{len}] - dp[i \cdot g][sum_{len}]$$$.
Асимптотика решения $$$O(maxA \cdot (K^2 + K \cdot \operatorname{log}(maxA)))$$$.
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using vi = std::vector<int>;
using vvi = std::vector<vi>;
using ll = long long;
const auto ready = []()
{
cin.tie(0);
std::ios_base::sync_with_stdio(false);
return true;
}();
ll binpow(ll a, ll p, ll mod)
{
ll res = 1;
ll mult = a;
while (p) {
if (p & 1) res = res * mult % mod;
mult = mult * mult % mod;
p >>= 1;
}
return res;
}
const ll mod = 998244353;
const int max_a = 2e5 + 10;
const int max_len = 6;
vi fi_div(max_a);
vi num_len(max_a);
void precalc() {
// Here we precompute the number of unique primes in each number, i.e. w(n)
for (int i = 2; i < max_a; ++i) {
if (fi_div[i] == 0) {
for (int j = i; j < max_a; j += i) {
if (fi_div[j] == 0) fi_div[j] = i;
}
}
}
for (int i = 2; i < max_a; ++i) {
int x = i;
int len = 0;
while (x > 1) {
++len;
int tmp = fi_div[x];
while (fi_div[x] == tmp) x /= tmp;
}
num_len[i] = len;
}
}
void solve() {
int n, k;
cin >> n >> k;
vi vec(n);
for(int i = 0; i < n; ++i) cin >> vec[i];
vvi cnt(n + 1, vi(max_len + 1));
for(int i = 0; i < n; ++i) {
int x = vec[i];
cnt[vec[i]][num_len[x]] += 1;
}
// Here we calcualte auxilary dp - cnt[x][len] - how many numbers with w(n) = len are divisible by x
for (int i = 1; i < n + 1; ++i) {
vi dp(max_len + 1);
for (int j = i; j < n + 1; j += i) {
for(int s =0 ; s < max_len + 1; ++s) dp[s] += cnt[j][s];
}
cnt[i] = dp;
}
vvi dp(n + 1, vi(2 * max_len + 1));
ll ans = 0LL;
// Here we calcualte dp[g][len]
for (int g = n; g >= 1; --g) {
// Account for cases len_a != len_b
for (int len1 = 0; len1 <= max_len; ++len1) {
for (int len2 = len1 + 1; len2 <= max_len; ++len2) {
dp[g][len1 + len2] += 1LL * cnt[g][len1] * cnt[g][len2] % mod;
dp[g][len1 + len2] %= mod;
}
}
// Account for cases len_a = len_b
for (int len = 0; len <= max_len; ++len) {
dp[g][len + len] += 1LL * cnt[g][len] * (cnt[g][len] - 1) / 2 % mod;
dp[g][len + len] %= mod;
}
// Subtruct states with where gcd is multiple of current g
for (int j = 2 * g; j < n + 1; j += g) {
for(int s = 0; s < 2 * max_len + 1; ++s) {
dp[g][s] -= dp[j][s];
dp[g][s] %= mod;
}
}
for(int s = 0; s < 2 * max_len + 1; ++s) {
dp[g][s] %= mod;
dp[g][s] += mod;
dp[g][s] %= mod;
}
// Add k-th powers to the answer
int gcd_len = num_len[g];
for (int len = 0; len < 2 * max_len + 1; ++len) {
int rad = len - gcd_len;
ans += dp[g][len] % mod * binpow(rad, k, mod) % mod;
ans %= mod;
}
}
cout << ans << "\n";
}
int main()
{
precalc();
int t;
cin >> t;
while (t--) solve();
return 0;
}
Разбор задач Codeforces Round 1070 (Div. 2)









Автокомментарий: текст был обновлен пользователем ABalobanov (предыдущая версия, новая версия, сравнить).
Auto comment: topic has been updated by ABalobanov (previous revision, new revision, compare).
Writing first so 123gjweq2 can't say first
I couldn't understand the tutorial code, so here's a (maybe) simpler top-down DP implementation with a very similar idea (compressing edge state into (end node index, start node value) pairs) for those like me. 353122473
Nice,,this code matches my idea
I thought of a different solution for problem D that barely passed in time.
Observe that the value written at a certain vertex is at most $$$10^{18}$$$. Therefore, no generalized Fibonacci path is longer than $$$86$$$ nodes, as the $$$87$$$-th non-generalized Fibonacci number is greater than $$$10^{18}$$$ (and this should hold for any other larger starting numbers than both being $$$1$$$). This can easily be checked with a program that calculates the first $$$n$$$ Fibonacci numbers.
Thus, we can store for each node the amount of paths that require the next node in a path to have a certain value, which is given by the sum of the value at the node and each adjacent node entering it, calculated in the previous iteration. Initially, accounting for paths of length 2, we can store at the destination of each edge all the sums between the value of that node and any node entering it, counting possibly repeated sums.
For convenience I used the reversed graph, as that way I could just check for a each node in an iteration if its value is present at the
mapof sums computed in the previous iteration for each of the outgoing nodes. Though that was just a decision I made while implementing during the contest.Due to the initial observation, there would be at most $$$87$$$ iterations over all the nodes and edges. Also, by using a map to lookup and update values present at a certain destination, I have a $$$\log$$$ factor in my solution to account for.
Hence, the complexity of this solution should be around $$$O(87 \cdot (n+m) \log m)$$$, considering in each iteration at most $$$m$$$ distinct sums would be computed (one for each edge), amounting to around $$$6.5 \cdot 10^8$$$ operations (explaining why my solution barely passed the TL). Also, I may be off in the exact numbers, but the general idea is that "Fibonnaci paths are short".
Here's my submission: 353082932
I tried to make use of the same idea (paths have length at most $$$87$$$) in some of my initial submissions but got TLE. After that, I realised I can just sort the vertices by $$$a_i$$$ and compute the DP in that order, so I very quickly modified the code to make it pass. Even the for-loop over the lengths is still in there!
Yup, after reading the editorial I got the idea of sorting edges by value in their destination, and I modified the submission I posted above. It passed in just 0.2s. The code is identical apart from that: 353380486
While in contest I also got a TLE, so I changed to a newer compiler, optimized access to maps a bit to avoid sometimes accessing two times unnecessarily, and crossed my fingers haha. Glad I got a bit lucky.
Nevertheless, I thought the idea was neat and worth mentioning.
Here is my two pass greedy linkedlist solution for E:
https://codeforces.me/contest/2176/submission/353120120
O(Nlog(N)) because of an initial sort, otherwise O(N).
I used the linked list to solve E too. Each time, choose the element that has the smallest removal cost and delete the elements on both sides whose natural values are not greater than the chosen element.
Here's my c++ implementation:
https://codeforces.me/contest/2176/submission/353133414
Sorry,what is the meaning of the natural values.
Oh... In the problem, $$$a_i$$$ is defined as the natural value.
Can anyone help me in question D?whats wrong in my approach
https://codeforces.me/contest/2176/submission/353126337
can anyone help me with problem D?
https://codeforces.me/contest/2176/submission/353126337
whats wrong here?intuitively seems perfect but getting wa on test 1 itself
Why my code fail on C.
what if we do not have enough evens :)
If even is used up, we gonna used $$$2$$$, $$$3$$$, $$$4$$$, …… odd numbers. But we don't want to use even times of odd numbers, let number of even number is $$$m$$$, when $$$k = m + 2$$$,i use three odd number and remove a minimum even number, when $$$k = m + c$$$ which $$$c$$$ is an odd number, the answer equals to $$$res_{m + 1}$$$. Why my code fail?
I get it. When I was modifying the code, I forgot to put this for loop outside the if statement.
I have a slightly different approach to D. I simply used DFS and memoization to find the answer. Since using map would give MLE (353062066), I used unordered map with custom hash function (353064160), which reduced the memory by a lot.
Can u help me memoise my code ? i think i was doing what u are thinking about ig. Anyway , see if you can help, thanks!
353249392
Use the dfs as a non-void function and memoise the answer to [idx, par], since the answer to [idx, par] doesn't change
Will do and get back to you. Thanks!
Can you tell me why am I getting TLE even with O(m) solution? 353299803
think like i have a graph like a star graph a middle node and left side has roughly m/2 nodes and right side also has m/2 nodes so your code will compute this type of graph answer in O(M/2 * M/2) which is O(M^2) that is the reason you are getting tle to avoid that you have to take them as a group and then propogate everyone value in the middle first and then propogate it to the next right half so it will reduce your time complexity to O(M/2 + M/2 ) which is O(M)
What is the complexity of your code though?
I think O(n + m)
Here is my solution to Problem D: Due to the rapid growth rate of Fibonacci, we can use DFS to calculate each Fibonacci number brute-force, and incorporate memoization to solve this problem. 353130807
having hints before main solution is really helpful, thanks
Convolution solution to F:
As mentioned, there are a number of solutions for F that simply use a standard method to AC the problem. One such method is the GCD convolution. This blog here has a very clean and nice description of the general ideas behind the zeta and mobius transforms used in the GCD convolution.
Generally speaking, the idea is very straightforward:
Here is my implementation: 353129856. Notice that although the code is a bit long, it's mostly just from boilerplate to determine the omega for each value and the code for the GCD convolution and the associated transforms (as an optimization, I didn't use the provided GCD convolution method but I'm still executing, well, a GCD convolution). The logical part in and of itself is relatively succinct.
In problem C, what is m?
m (in the editorial) is the number of even numbers in the array
Ah okay. Thank you!
Can someone tell why my code is giving wrong answer on test case 2 or give a test case where my code fails
I have found your bug!
Its because you had this line:
if(even.size()==1) val[k]=0The counterexample is this case
n=5a = {1, 1, 1, 1, 2}The AC code outputs:
1 3 1 3 0But your code outputs:1 3 0 3 0Thank you
E can be solved using segment tree beats.
For each
ifindl,rwhich is the widest range (includingi) that have values<=a[i](using stack). Then update the cost for removing each element in rangeltorby assigning it tomin(cost[j],c[i])for which we use segtree beats. Initial answer would besum of cost for all minus cost for last remaining element(which is minimum ofc[j]wherea[j]==max(a)). For zeroing operations, you usel,rof that element and updatecost[j]tomin(cost[j],0)and again find the sum of all costs. Once you have zeroed any index such thata[i]==max(a)all further costs will be0.This solution can be used to solve for a type of updation query where you reduce
cfor any element.Finally Green Again.
can anyone tell me, what is the error in this code ~~~~~ // this is code
include<bits/stdc++.h>
using namespace std; typedef long long ll; int main() { ll t,n,i,odd,even,sum,k,check; cin>>t; while(t--) { cin>>n; ll array[n]; odd=0,even=0; vectorodds,evens; for(i=0;i<n;i++) { cin>>array[i]; if(array[i]%2==1) { odd++; odds.push_back(array[i]); } else { even++; evens.push_back(array[i]); } } sort(odds.begin(),odds.end(),greater()); sort(evens.begin(),evens.end(),greater()); vectorpresum; sum=0; for(i=0;i<evens.size();i++) { sum+=evens[i]; presum.push_back(sum); } if(odd==0) { for(i=1;i<=n;i++) cout<<0<<" "; cout<<endl; } else if(even==0) { for(i=1;i<=n;i++) { if(i%2==1) cout<<odds[0]<<" "; else cout<<0<<" "; } cout<<endl; } else { if(odd==1) { cout<<odds[0]<<" "; for(i=0;i<presum.size();i++) cout<<odds[0]+presum[i]<<" "; cout<<endl; } else if(odd==2) { cout<<odds[0]<<" "; for(i=0;i<presum.size();i++) cout<<odds[0]+presum[i]<<" "; cout<<0<<" "<<endl; } else {// odds>=3 cout<<odds[0]<<" "; for(i=0;i<presum.size();i++) cout<<odds[0]+presum[i]<<" "; k=1+presum.size(); check=(k%2); for(i=k+1;i<=n;i++) { if((i%2)==check) cout<<odds[0]+presum[presum.size()-1]<<" "; else cout<<odds[0]+presum[presum.size()-2]<<" "; } cout<<endl; } } } return 0; } ~~~~~
fails for test case t=1,n=7,a=
6 4 2 1 3 5 7where k=n.And the answer for s_tapan099's tc is
7 13 17 19 17 19 0A nice observation can make code for C easier. Here is my submission 353059457 Following the nomenclature in tutorial since for k=m+2 we are using oc and all even except mth therefore it is same for k=m and again for k=m+3 it is same as k=m+1 and so on. All edge cases considered separately — All odd, no odd and even no.of odds.
The hints of problem c are very good.
I like it how he explain all the cases in problem c.
I saw 998345353 at F when I was using a translate plugin. Who proposed the idea? Great!
For E there is a O(n) solution, you do cartesian tree and color the subtree of a node each time, with ascending
c[i]'s. The only caveat is ifa[node] == a[par[node]]you should start coloring frompar[node]so for that I hold alinkarray which gets us to the highest node with the same value as the current one. Here is my solution : https://codeforces.me/contest/2176/submission/353155958note : I did the sorting with
std::sortfor convenience but you can do that with counting sort to get true O(n)In Problem D, why we should sort the edges by $$$a_u+a_v$$$?
if you have a simple path and it follows the definition of fibonacci sequence, and without loss of generality lets assume if edge (u1,v1) comes before edge (u2, v2) then its sum should also be in the same order.
I spent quite a bit of time on Problem B, only to see a three-line solution in the editorial…
For reference, this is the method I used:
Here’s my submission:B — Optimal Shifts
Can anyone confirm if this actually minimizes the number of operations (not the cost)? Would feel better knowing I wasn’t overcomplicating things.
It turns out there is no need for 87 or 90 bound needed in problem $$$D$$$. The problem doesn't guarantees that the given graph is DAG , but we can still make it a DAG. Note that the sequence is increasing except for the first two elements. Hence make a second graph , which has an edge from $$$u$$$ to $$$v$$$ iff $$$a[v] \gt a[u]$$$. You can look at my submission 353070891
why is my solution failing in problem D
my code : 353225582
I met the same problem with you, just Wrong Answer On test 3 the 251st case. But I don't know why.
For C I think you it doesn't matter which odd number it is, except for the maximum. If at least one odd exists, you could first print the maximum odd number and then m more numbers where the ith number will be maxOdd+ith even number (in descending order) and then for the remaining numbers: m+2 to n, for jth number if (j-m) is odd print the (sum of all even+maxOdd) else print 0, and add check that if there are even number of odd numbers then nth number will be 0 regardless of the parity of j-m.
353291565
I am getting TLE , please help in problem D
Nice editorial for F.
In problem C, cnteven is for what??
What is the "A well-known method can be used to count the number of pairs of numbers in the array (i,j) for which gcd(ai,aj)=g for any number g from 1 to maxA" in F. Can someone say how I can google it or provide a link to editorial of this method?
Well, I think I understood what method was mentioned here. In short, this is a regular dp, where we count the number of pairs that x divides, and then subtract all the other gcds that x divides. This can be done as in sieve of Eratosthenes, and it will work for O(nlogn)
Easy solution for D: Let's replace each edge v->u with an edge between two FAKE vertices: (v << 64 | Y) -> (u << 64 | X + Y), where X is the value associated with vertex v, and Y with u. This way, we obtain a new graph in which ANY path corresponds to a Fibonacci path! Simply count the number of paths in this graph using DFS and caching. https://codeforces.me/contest/2176/submission/353494951
Can you guys tell me why my D is TLEing?
PROBLEM D: I am getting TLE on test case 11 353509271. What can I do better?
For F, a slightly better bound on K can be given. The primorial function is bigger than n! so it's inverse is less. By Stirling, n! is $$$O\left(e^{n \ln(n)}\right)$$$ and the inverse of this is $$$O\left(\log(n)/\log(\log(n))\right)$$$
I've found, I guess, very simple linear solution to problem E.
Let's see that an element can be killed only by first not-less element on the right|left, or first not-less than first not-less on the right|left, or ...
We will denote the set of elements, which are able to kill element at position $$$i$$$, as $$$killers(i)$$$.
Let's assign for each $$$i$$$ the cheapest element from $$$killers(i)$$$ to kill $$$i$$$.
The observation: there is a sequence of deletions which satisfies our assignments.
The proof's sketch: Let's draw an arrow from each position i to his killer. It suffices to show that there are no crossing arrows. We can see that if two arrows are crossing, then we can reassign one of them and obtain cheaper solution.
It is known that we can compute "first not-less on right|left" in linear time. With a simple DP we can also compute those assignments in $$$O(n)$$$. The answer is just a sum of costs of killing each element. So we solved E without queries.
We can now observe that any changes in elements that doesn't belong to $$$killers(i)$$$ don't have any impact on the cost of killing $$$i$$$. Hence it suffices to compute, for each element, the first time that some member of $$$killers(i)$$$ is changed to zero. We can solve it in $$$O(n)$$$ with very similar DP.
I thought of a different solution for problem C .
The approach is to separate the numbers into even and odd and sort even numbers in descending order and odd numbers in descending order. If there are no odd numbers then the answer would be zero for all k. If there are no even numbers then the answer would be zero for even k and the largest odd number for odd k. We need the sum of even numbers to reduce the O(n) complexity to calculate again, so prefix sums of even numbers are used. For each k, first start by placing the largest odd number. If the number of evens available are enough then just add the number of even numbers required in descending order. If the number of even numbers available are less than required, then if the number of odd numbers to be used are even, simply place them before the largest odd number as their sum would give 0, so the answer should be the largest odd number plus the sum of all even numbers. If the number of odd numbers to be used are odd, then simply remove the smallest even number and place all odd numbers available before the largest odd number, and if k equals n (that is all numbers are to be used) then the sum would be 0.
Here is my submission :
https://codeforces.me/contest/2176/submission/356942161
Problem C. Odd Process
Can anybody explain sample cases 3 and 4?
Input:
Output:
In sample 3, for example, on $$$k=4$$$, doesn't the bag contain $$$[3, 4, 2, 1]$$$, with the even sum $$$10$$$, therefore being emptied and giving a score of $$$0$$$? How is the score $$$7$$$ instead? Same confusion with sample 4.
For $$$k=4$$$ we can take $$$[1, 1, 3, 4]$$$ in this order. Maybe you miss that we can solve problem independently from previous $$$k$$$
You are right. I missed the point that for each $$$k$$$ we begin with an empty bag. Thanks for the clarification.
4
A Better Approach for Problem C 2176C - ODD PROCESS - Odd Process LYC_666 ~~~~~ #include<bits/stdc++.h> using namespace std;
define int long long
bool cmp(int t1,int t2) { return t1 > t2; }
signed main() { ios::sync_with_stdio(0), cin.tie(0); int t; cin >> t; while (t--) { int n; cin >> n; vectorodd, even; for (int i = 0; i < n; i++) { int v; cin >> v; if (v % 2 == 0) { even.push_back(v); } else { odd.push_back(v); } } if (odd.empty()) { for (int i = 0; i < n; i++) { cout << "0 "; } cout << "\n"; continue; } sort(even.begin(), even.end(),cmp), sort(odd.begin(), odd.end(), cmp); int size = even.size() + 1; vectorpre(size + 1); pre[1] = odd[0]; for (int i = 0; i < even.size(); i++) { pre[i + 2] = pre[i + 1] + even[i]; } for (int i = 1; i < n; i++) { if (i <= size) { cout << pre[i]<<" "; } else { if ((i — size) % 2 == 1) { cout << pre[size — 1] << " "; } else { cout << pre[size] << " "; } } } if (odd.size() % 2 == 0) { cout << "0"; } else { cout << pre[size]; } cout << "\n"; } return 0; } ~~~~~
359184091
simpler submission for d https://codeforces.me/contest/2176/submission/382217189
B can be solved using simple BFS 386692869