Назовём массив неоднозначным, если существует стро...
Erstellt am: 15. Februar 2025
Beantwortet mit O3 Mini High von Chat01
Erstellt am: 15. Februar 2025
Beantwortet mit O3 Mini High von Chat01
Назовём массив неоднозначным, если существует строго больше одного способа, разбить его
а два непустых подмассива, так что оба этих подмассива являются перестановками. Обратите
внимание, что каждый элемент массива должен войти в ровно один подмассив из разбиения, при
этом каждый подмассив должен состоять из последовательных элементов исходного массива.
Например, массив [1,2,1] является неоднозначным, его можно разбить как и на [1] + [2,1], так и
на [1,2] + [1].
Вам дан массив a1, 02, . .. ,an. За одну операцию вы можете изменить любой элемент массива на
любое целое число от 1 до п. Какое минимальное количество операций необходимо, чтобы сделать
массив a1, а2, ... ,ап неоднозначным?
Напомним, что массив р1, P2, ··· ,Pn называется перестановкой, если для каждого х от 1 до n,
существует ровно одно і такое, что р; = х.
Формат входных данных
Первая строка входных данных содержит целое число n (3 <n < 10') - длина массива а и целое
число f, которое описывает формат, в котором вам будет задан массив а.
Если f = 1, формат стандартный, и тогда:
Вторая строка входных данных содержит n целых чисел a1, a2, . .. , an (1 <ai <n) - элементы
массива а.
Если же f = 2, формат таков:
Вторая строка входных данных содержит целое число m (3 <m < min(n, 2 · 105)) и три целых
числа x, y, z (0 <x, y,z<п).
Третья строка входных данных содержит т целых чисел b1, b2, ... , bm (1 <b; < n).
Значения а1, а2, ... , an - массив, для которого вам нужно найти ответ в задаче, считаются сле-
дующим образом:
· Для1<i<m: ai=bi
· Для т < i < n: ai = 1 + (x · ai-2 + y - ai-1 +z) mod n
реши на c++
Мы можем решить задачу, сведя её к следующему наблюдению.
Предположим, что массив после изменений имеет по крайней мере два способа разбить себя на два непустых подмассива, каждый из которых является перестановкой. Легко заметить, что если рассмотреть разбиения по индексу (то есть и ) условие того, что обе части – перестановки, означает:
Оказывается, что если в массиве существует более одного способа такого разбиения, то они обязательно имеют вид «слева маленький кусок» и «справа маленький кусок»: то есть существуют с такие, что
Действительно, если рассмотреть два разбиения (например, по индексам и , где ), то условие, что оба разбиения корректны, приводит к тому, что на пересечениях вынуждены появляться именно эти наборы чисел.
Пусть мы хотим получить такое решение с минимальным числом операций (замен элементов). Заметим, что мы можем независимо «подправлять» три участка:
Префикс должен стать перестановкой чисел . Если на данном участке уже присутствуют некоторые числа из множества (каждое можно использовать только один раз), то мы можем «сохранить» их – другими словами, экономим операцию. Максимальное число позиций, которые можно не трогать, равно количеству различных чисел из , появившихся в (каждое считается не более одного раза).
Аналогично, суффикс должен стать перестановкой чисел . Его можно «сохранить» в количестве – число различных , которые встречаются хотя бы один раз в .
Средний участок должен стать перестановкой чисел . Здесь можно сохранить те из этого множества, которые встречаются хотя бы раз на участке. При этом можно показать, что для данного условие появления в участке равносильно тому, что для этого выполняется неравенство
где – первый индекс появления в массиве , а – последний.
Таким образом, если для каждого участка знать «сбережения» (то есть число позиций, где не надо менять элемент), то стоимость корректировки участка – длина участка минус число сбережённых позиций.
Заметим ещё – поскольку разбиение должно быть неоднозначным – достаточно найти такое с для которого суммарная стоимость (префикс + средний участок + суффикс) минимальна.
Чтобы вычислить «сбережения» эффективно, можно сделать следующее:
(A) Префикс.
Для «сохраняем», если встречается в . Это равносильно тому, что первый индекс появления (обозначим ) не больше . Тогда общее число сбережённых позиций – это число таких (при этом каждый можно посчитать ровно один раз).
Чтобы быстро вычислять для всех сумму по индикатора – удобно воспользоваться разностным массивом: для каждого добавляем единицу начиная с .
(B) Суффикс.
Аналогично – для сохраняем, если встречается в . Это равносильно тому, что . Опять можно использовать разностный массив: для каждого прибавляем единицу начиная с .
(C) Средний участок.
Здесь разрешённые числа – (их ровно ). Для каждого числа (при условии, что появляется в ) можно доказать, что будет встречаться в для всех от до
Тогда если для каждого посчитать и с помощью разностного массива (или суммирования по группам) построить массив, где для каждого узнаём число с , то получим число сбережённых позиций на среднем участке.
Обозначим:
Тогда суммарная стоимость для данного равна:
Ответ — минимум по .
Ниже приведён полный код на C++ (с комментариями), который решает задачу. (Обратите внимание – код поддерживает оба формата входных данных, когда – стандартный список, и – генерация по рекуррентной формуле.)
cpp#include <bits/stdc++.h> using namespace std; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, f; if(!(cin >> n >> f)) return 1; // Массив a будем считать с индексами от 1 до n. vector<int> a(n+1, 0); if(f == 1){ for (int i = 1; i <= n; i++){ cin >> a[i]; } } else { // f == 2 int m; long long X, Y, Z; cin >> m >> X >> Y >> Z; for (int i = 1; i <= m; i++){ cin >> a[i]; } // Для i от m+1 до n: a[i] = 1 + (X * a[i-2] + Y * a[i-1] + Z) mod n. for (int i = m+1; i <= n; i++){ long long val = (X * a[i-2] + Y * a[i-1] + Z) % n; a[i] = (int)(val + 1); } } // 1. Для каждого числа x (от 1 до n) находим: // occ[x] = первый индекс, где a[i]==x (если не встретилось, = n+1) // last[x] = последний индекс, где a[i]==x (если не встретилось, = 0) vector<int> occ(n+1, n+1), last(n+1, 0); for (int i = 1; i <= n; i++){ int val = a[i]; if(val < 1 || val > n) continue; occ[val] = min(occ[val], i); last[val] = i; } // 2. Для префикса: x (от 1 до n) можно "сохранить" на префиксе длины k, // если k >= max(x, occ[x]). Используем разностный массив. vector<int> diffPrefix(n+2, 0); for (int x = 1; x <= n; x++){ if(occ[x] <= n){ int start = max(x, occ[x]); if(start <= n) diffPrefix[start]++; } } vector<int> prefixSave(n+1, 0); for (int k = 1; k <= n; k++){ prefixSave[k] = diffPrefix[k] + (k > 1 ? prefixSave[k-1] : 0); } // Тогда cost_pref(k) = k - prefixSave[k]. // 3. Аналогично для суффикса (индексы n-k+1..n, допустимые числа 1..k): // x сохраняется, если k >= max(x, n - last[x] + 1). vector<int> diffSuffix(n+2, 0); for (int x = 1; x <= n; x++){ if(last[x] > 0){ int start = max(x, n - last[x] + 1); if(start <= n) diffSuffix[start]++; } } vector<int> suffixSave(n+1, 0); for (int k = 1; k <= n; k++){ suffixSave[k] = diffSuffix[k] + (k > 1 ? suffixSave[k-1] : 0); } // Тогда cost_suff(k) = k - suffixSave[k]. // 4. Для среднего участка [k+1, n-k] (размер n-2k, допустимые числа k+1..n-k). // Для числа x, которое встречается, пусть L = occ[x] и R = last[x]. // x появится на среднем участке, если существует i \in [k+1, n-k] с a[i]==x. // Это равносильно условиям: x \in [k+1, n-k], R >= k+1 и L <= n-k. // Тогда x будет присутствовать для всех k таких, что: // k <= min(x-1, n-x, n - occ[x], last[x]-1). // Обозначим T_x = min(x-1, n-x, n - occ[x], last[x]-1). // Тогда x "сохраняется" на среднем участке для всех k от 1 до T_x. // С помощью разностного метода (или аккумулирования) можно посчитать для каждого k число x с T_x >= k. vector<int> countT(n+1, 0); for (int x = 1; x <= n; x++){ if(last[x] == 0) continue; int t1 = x - 1; int t2 = n - x; int t3 = n - occ[x]; int t4 = last[x] - 1; int T = min({t1, t2, t3, t4}); if(T < 1) continue; if(T > n) T = n; countT[T]++; } // Построим суффиксную сумму: для каждого k будем знать число x таких, что T_x >= k. vector<int> suffixCount(n+2, 0); suffixCount[n] = countT[n]; for (int t = n-1; t >= 1; t--){ suffixCount[t] = countT[t] + suffixCount[t+1]; } // Для среднего участка: cost_mid(k) = (n - 2*k) - (число сбережённых), // где число сбережённых = suffixCount[k]. // 5. Перебираем все возможные k, где k от 1 до floor((n-1)/2) (т.к. 2*k < n) int maxK = (n - 1) / 2; int ans = INT_MAX; for (int k = 1; k <= maxK; k++){ int costPrefix = k - prefixSave[k]; int costSuffix = k - suffixSave[k]; int segLength = n - 2 * k; int saveMiddle = suffixCount[k]; // число x с T_x >= k int costMiddle = segLength - saveMiddle; int total = costPrefix + costMiddle + costSuffix; ans = min(ans, total); } cout << ans << "\n"; return 0; }
Ввод и генерация массива.
Если – читаем массив напрямую, иначе (при ) по заданной схеме генерируем оставшиеся элементы.
Предварительная обработка.
Для каждого вычисляем (первое появление) и (последнее). Это нужно для оценки «сбережённых» позиций на каждом из трёх участков.
Подсчёт сбережений.
Для префикса и суффикса с помощью разностных массивов определяем, сколько разрешённых чисел (каждое считать один раз) уже встречается. Для среднего участка для каждого считаем – для всех число можно «сохранить».
Перебор .
Для каждого допустимого (с ) вычисляем суммарную стоимость исправлений и выбираем минимум.
Этот код решает задачу за (с дополнительными O(n) при генерации и суммировании) и подходит для ограничений задачи.
Вы можете скопировать и скомпилировать данный код на C++ (стандарт C++11 и выше).