Ограничение по времени: 1 секунда Ограничение по п...
創建於:2026年9月17日
創建於:2026年9月17日
Ограничение по времени: 1 секунда
Ограничение по памяти: 256 мегабайт
Когда Пеннивайз решил испугать Эдди, он даже немного растерялся. Дело в том, что у Эдди
было огромное количество страхов, и Пеннивайз даже не знал, за какой страх ему зацепиться.
Немного подумав, Пеннивайз понял, что Эдди боится различных элементов в массиве.
У Эдди есть массив a длины n. За один ход он может умножить любой элемент массива на произвольное натуральное число. Теперь для каждого k от 0 до n включительно выведите минимальное
количество различных элементов, способных получиться ровно после k ходов.
Формат входных данных
Первая строка ввода содержит целое число n (1 6 n 6 3 · 105) — длину массива.
Следующая строка содержит n целых чисел ai (1 6 ai 6 106) — элементы массива.
Формат выходных данных
В единственной строке выведите n + 1 целых чисел. i-е число должно содержать ответ после
хода i − 1.
Пример
стандартный ввод стандартный вывод
6
3 4 1 2 1 2
4 4 3 3 2 2 1
Замечание
Подзадачи:
дай идею как решить
Идея довольно красивая. Сожмём массив по значениям: пусть cnt[x] — сколько раз встречается x, а D — число различных значений.
Если мы хотим полностью убрать значение x из массива, нужно изменить все cnt[x] его вхождений. Значит, «стоимость удаления значения x» равна cnt[x].
При этом изменённое x можно превратить в любое кратное x.
Если в итоговом массиве остаётся значение y, то любое x | y можно целиком превратить в y.
Поэтому x можно удалить за cnt[x] ходов, не увеличивая число различных элементов, если существует другое присутствующее значение y, такое что
Назовём значение максимальным, если среди присутствующих значений нет другого его кратного.
Например, для
text1 2 3 4
максимальные — 3 и 4.
Причём важный факт:
Если оставить все максимальные значения, то любое немаксимальное значение можно удалить независимо от остальных.
Почему? Если x немаксимально, существует большее присутствующее кратное. Поднимаясь по кратным, рано или поздно придём к максимальному значению y, причём x | y.
Таким образом, среди немаксимальных значений мы просто хотим удалить как можно больше значений при бюджете k. Стоимость удаления x — cnt[x].
Значит, нужно взять самые маленькие cnt[x].
Да, но тогда его уже некуда превратить среди оставшихся исходных значений.
Однако все такие «проблемные» изменённые значения можно собрать в одно новое значение — например, в их НОК (конкретно считать НОК не нужно).
Если мы полностью удалили t различных исходных значений, но среди них есть хотя бы одно максимальное, то эти t значений заменятся минимум одним новым.
То есть число различных уменьшается не на t, а на
Следовательно, есть два варианта.
t штук, выигрыш = t.t самых дешёвых вообще, выигрыш = t-1.Ответ:
Для t_all = 0 считаем второй выигрыш равным 0.
Есть present[x].
Для каждого присутствующего x проверяем его кратные:
cppfor (int y = 2*x; y <= MAX; y += x) if (present[y]) x is non-maximal;
Суммарная сложность:
где M <= 10^6, это нормально.
После этого собираем два массива стоимостей:
textall = cnt[x] для всех различных x nonmax = cnt[x] только для немаксимальных x
сортируем их и строим префиксные суммы.
Для каждого k ищем максимальное число элементов, чей префикс <= k.
На примере:
text3 4 1 2 1 2
частоты:
text1 -> 2 2 -> 2 3 -> 1 4 -> 1
Максимальные по делимости: 3, 4.
Немаксимальные: 1, 2, их стоимости:
text[2, 2]
Все стоимости:
text[1, 1, 2, 2]
Например, при k=2 можно удалить два значения с частотой 1, то есть 3 и 4, но они сольются в одно новое значение: выигрыш 2-1=1, ответ 4-1=3.
При k=4 можно удалить 1 и 2, превратив их, например, в 4: выигрыш 2, ответ 2.
Получаем:
text4 4 3 3 2 2 1
Итоговая сложность — примерно O(M log M + n log n), память O(M+n).
1 докажи поиск максимальных что там MLOG
потом напиши код в моём стиле вот пример решение пеши только в sovle
#include <bits/stdc++.h>
#define int long long
using namespace std;
using i128 = __int128;
using ld = long double;
const int INF = 1e15;
const int N = 1e5 + 7;
const int MOD = 1e9 + 7;
void solve() {
int k;
cin >> k;
vector< bool > b(N);
vector< int > pr;
for (int i = 2; i < N; i++) {
if (!b[i]) {
pr.push_back(i);
textint j = i + i; while (j < N) { b[j] = 1; j += i; } }
}
int d = k;
vector< int > vos(pr.size(), 0);
for (int i = 0; i < pr.size(); i++) {
while (d % pr[i] == 0) {
vos[i]++;
d /= pr[i];
}
vos[i] *= 2;
}
if (d != 1) {
pr.push_back(d);
vos.push_back(2);
}
set< pair< int, int > > ans;
function< void(int, int) > f = [&](int x, int i) {
if (i + 1 >= vos.size()) {
int b = x + k;
int a = (k * k) / (b - k) + k;
ans.insert({a, b});
return;
}
while (vos[i + 1] == 0) {
i++;
if (i + 1 >= vos.size()) {
int b = x + k;
int a = (k * k) / (b - k) + k;
ans.insert({a, b});
return;
}
}
textint cnt = vos[i + 1] + 1; while (cnt--) { f(x, i + 1); if (cnt) x *= pr[i + 1]; }
};
f(1, -1);
cout << ans.size() << "\n";
for (auto [a, b] : ans) {
cout << a << " " << b << "\n";
}
}
int32_t main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
srand(time(0));
int tests = 1;
//cin >> tests;
while (tests--) {
solve();
}
return 0;
}
/*
int d[2][2][700] = {i, j, x};
*/
Поиск максимальных по делимости работает так:
cppfor (int x = 1; x <= M; x++) { if (!cnt[x]) continue; for (int y = x + x; y <= M; y += x) { if (cnt[y]) { // x не максимальный } } }
Для фиксированного x мы перебираем
то есть не более
чисел.
Поэтому общее количество итераций не больше
А гармоническая сумма
Например, это можно увидеть группировкой:
В каждой группе максимум 1, а групп всего O(log M).
Значит,
Причём у нас цикл запускается только для присутствующих x, так что фактически итераций даже меньше.
Код в твоём стиле, только solve:
cppvoid solve() { int n; cin >> n; vector< int > a(n); int mx = 0; for (int i = 0; i < n; i++) { cin >> a[i]; mx = max(mx, a[i]); } vector< int > cnt(mx + 1); for (int x : a) { cnt[x]++; } int dif = 0; for (int x = 1; x <= mx; x++) { if (cnt[x]) dif++; } vector< bool > bad(mx + 1); for (int x = 1; x <= mx; x++) { if (!cnt[x]) continue; for (int y = x + x; y <= mx; y += x) { if (cnt[y]) { bad[x] = 1; break; } } } vector< int > all; vector< int > nonmax; for (int x = 1; x <= mx; x++) { if (!cnt[x]) continue; all.push_back(cnt[x]); if (bad[x]) { nonmax.push_back(cnt[x]); } } sort(all.begin(), all.end()); sort(nonmax.begin(), nonmax.end()); vector< int > pa(all.size() + 1); vector< int > pn(nonmax.size() + 1); for (int i = 0; i < all.size(); i++) { pa[i + 1] = pa[i] + all[i]; } for (int i = 0; i < nonmax.size(); i++) { pn[i + 1] = pn[i] + nonmax[i]; } for (int k = 0; k <= n; k++) { int x = upper_bound(pn.begin(), pn.end(), k) - pn.begin() - 1; int y = upper_bound(pa.begin(), pa.end(), k) - pa.begin() - 1; int del = x; if (y) { del = max(del, y - 1); } cout << dif - del << " "; } cout << "\n"; }
Ещё один момент: break внутри поиска кратного не портит оценку — он только уменьшает число итераций, поэтому верхняя граница всё равно O(M log M).