Редакция для Пары с заданной суммой
Submitting an official solution before solving the problem yourself is a bannable offence.
Автор:
1. Идея
Нужно посчитать количество пар индексов (i, j), где i < j и c_i + c_j = X.
Перебирать все пары напрямую можно за O(n^2), но есть способ быстрее.
Будем идти по массиву слева направо и для каждой монеты c_i спрашивать:
- сколько монет с номиналом
X - c_iуже встретилось раньше?
Каждая такая ранее встреченная монета образует с текущей подходящую пару.
Для этого удобно хранить в словаре количество уже встреченных значений.
2. Наблюдения
Наблюдение 1
Если текущая монета равна value, то ей нужна монета со значением need = X - value.
Если раньше уже встретилось k таких монет, то текущая монета добавляет ровно k новых пар.
Наблюдение 2
Важно считать только пары с i < j.
Если идти слева направо, то в словаре лежат только элементы с меньшими индексами. Значит, каждая пара будет учтена ровно один раз — в момент обработки правого элемента пары.
Наблюдение 3
Одинаковые значения обрабатываются естественно.
Например, если X = 6, а в массиве есть числа 3 3 3, то:
- первый
3не образует пар; - второй
3образует 1 пару с первым; - третий
3образует 2 пары с предыдущими.
Итого получается 3 пары, что верно.
3. Алгоритм
- Считываем
n,Xи массив монет. - Создаём словарь
cnt, гдеcnt[v]— сколько раз значениеvуже встретилось. - Создаём переменную
answer = 0. - Для каждого элемента
valueмассива:- вычисляем
need = X - value; - добавляем к ответу
cnt[need], если такое значение уже встречалось; - увеличиваем
cnt[value]на 1.
- вычисляем
- Выводим
answer.
4. Почему это работает
Докажем, что алгоритм считает ответ правильно.
Рассмотрим момент обработки элемента c_j.
- В словаре
cntуже хранятся количества всех элементовc_0, c_1, ..., c_{j-1}. - Чтобы получить сумму
X, нужен элементc_i = X - c_j. - Количество таких индексов
i, гдеi < j, равноcnt[X - c_j].
Значит, при обработке c_j мы добавляем ровно число всех пар (i, j), заканчивающихся в j.
Так происходит для каждого j, поэтому:
- каждая подходящая пара будет учтена,
- ни одна пара не будет учтена дважды, потому что учитывается только при обработке второго элемента.
Следовательно, итоговое значение answer равно количеству всех пар (i, j), где i < j и c_i + c_j = X.
5. Сложность
Для каждого из n элементов выполняется фиксированное число операций со словарём.
- Время:
O(n)в среднем. - Память:
O(n).
При n <= 5000 такое решение работает с большим запасом.
6. Код на C++17
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
int main() {
int n;
long long X;
cin >> n >> X;
vector<long long> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
unordered_map<long long, long long> cnt;
long long answer = 0;
for (int i = 0; i < n; i++) {
long long need = X - a[i];
if (cnt.find(need) != cnt.end()) {
answer += cnt[need];
}
cnt[a[i]]++;
}
cout << answer << '\n';
return 0;
}
7. Код на Python 3
n, X = map(int, input().split())
a = list(map(int, input().split()))
cnt = {}
answer = 0
for value in a:
need = X - value
answer += cnt.get(need, 0)
cnt[value] = cnt.get(value, 0) + 1
print(answer)
Комментарии