Ниже приводжу подробное решение задачи и готовый код. Введу все необходимые определения, тогда дам формулы и итоговый алгоритм, а затем пример реализации на языке Rust 1.56.
Цель задачи
- Посчитать коэффициент работоспособности клавиатуры по определениям:
- Хорошая пара: k_i = k_j и c_i = c_j
- Почти хорошая: k_i ≠ k_j и c_i ≠ c_j
- Плохая: k_i = k_j и c_i ≠ c_j
- Почти плохая: k_i ≠ k_j и c_i = c_j
- Обозначим:
- G — число хороших пар
- P — число почти хороших пар
- B — число плохих пар
- N — число почти плохих пар
- Коэффициент: (√G + √P) / (√B + √N)
- Если √B + √N = 0, клавиатура считается полностью рабочей и выводим -1.
Замечания по вычислениям
- Всего пар между N нажатиями: T = C(N, 2) = N(N-1)/2.
- Введём частоты:
- freq_k[k] — сколько раз встречается номер клавиши k
- freq_c[c] — сколько раз встречается код c
- freq_kc[(k,c)] — сколько раз встречается пара (k, c)
- Вычислим нужные суммы:
- G = sum over (k,c) of C(freq_kc[(k,c)], 2)
- S_k = sum over k of C(freq_k[k], 2)
- S_c = sum over c of C(freq_c[c], 2)
- B = S_k - G
- A = N-плохих? нет, Almost Bad — A = S_c - G
- P = T - S_k - S_c + G
- Примечание: A соответствует "почти плохим" и мы будем называть его A.
Проверки и вывод
- Если A == 0 и B == 0, выводим -1 (denominator равен нулю).
- Иначе вычисляем:
- Gf = G, Pf = P, Af = A, Bf = B в виде f64
- coef = (sqrt(Gf) + sqrt(Pf)) / (sqrt(Bf) + sqrt(Af))
- Выводим с достаточной точностью (например, 12 знаков после запятой).
Сложность
- Построение частот занимает O(N).
- Подсчёт G, S_k, S_c требует прохождения по элементам словарей, общая сложность O(N) ожидаемо.
- Память: O(U), где U — число уникальных значений k, уникальных c и уникальных пар (k,c). В рамках ограничений N ≤ 1e5.
Решение на Rust 1.56
use std::collections::HashMap;
use std::io::{self, Read};
fn main() {
// Считываем вход
let mut input = String::new();
io::stdin().read_to_string(&mut input).unwrap();
let mut it = input.split_whitespace();
let n: usize = it.next().unwrap().parse().unwrap();
let mut ks: Vec<i64> = Vec::with_capacity(n);
for _ in 0..n {
let v: i64 = it.next().unwrap().parse().unwrap();
ks.push(v);
}
let mut cs: Vec<i64> = Vec::with_capacity(n);
for _ in 0..n {
let v: i64 = it.next().unwrap().parse().unwrap();
cs.push(v);
}
// Частоты
let mut freq_k: HashMap<i64, u64> = HashMap::new();
let mut freq_c: HashMap<i64, u64> = HashMap::new();
let mut freq_kc: HashMap<(i64, i64), u64> = HashMap::new();
for i in 0..n {
*freq_k.entry(ks[i]).or_insert(0) += 1;
*freq_c.entry(cs[i]).or_insert(0) += 1;
let key = (ks[i], cs[i]);
*freq_kc.entry(key).or_insert(0) += 1;
}
// G: количество пар с одинаковыми (k,c)
let mut g: u128 = 0;
for &f in freq_kc.values() {
g += (f as u128) * ((f - 1) as u128) / 2;
}
// S_k: количество пар с одинаковым k
let mut sk: u128 = 0;
for &f in freq_k.values() {
sk += (f as u128) * ((f - 1) as u128) / 2;
}
// S_c: количество пар с одинаковым c
let mut sc: u128 = 0;
for &f in freq_c.values() {
sc += (f as u128) * ((f - 1) as u128) / 2;
}
let nn = n as u128;
let t: u128 = nn * (nn - 1) / 2;
// B = S_k - G
let b: u128 = if sk >= g { sk - g } else { 0 };
// A (almost bad) = S_c - G
let a: u128 = if sc >= g { sc - g } else { 0 };
// P = T - S_k - S_c + G
let p_signed: i128 = (t as i128) - (sk as i128) - (sc as i128) + (g as i128);
let p: u128 = if p_signed < 0 { 0 } else { p_signed as u128 };
// Если denom равен нулю, клавиатура полностью рабочая
if a == 0 && b == 0 {
println!("-1");
return;
}
// Вычисляем коэффициент
let coef_numer = (g as f64).sqrt() + (p as f64).sqrt();
let coef_denom = (b as f64).sqrt() + (a as f64).sqrt();
let result = coef_numer / coef_denom;
// Вывод с достаточной точностью
println!("{:.12}", result);
}
Пояснение к коду
- Сначала строим три частотных отображения:
- freq_k: сколько раз встречается каждый номер клавиши k
- freq_c: сколько раз встречается каждый код c
- freq_kc: сколько раз встречается конкретная пара (k, c)
- Затем вычисляем G, S_k, S_c по формулам C(x, 2) = x(x-1)/2.
- Находим B = S_k - G, A = S_c - G, P = T - S_k - S_c + G.
- Если A и B равны нулю, выводим -1. Иначе считаем коэффициент по формуле (√G + √P) / (√B + √A).
Как адаптировать под параметры задачи
- Если известны конкретные предметы или класс, стиль объяснения можно сделать более формальным и с примерами именно по данной теме (например, вероятности, комбинаторика, частоты).
- В данной реализации ориентирован на общую школьную задачу: объяснение шагов, затем решение и код.
Пример небольшого теста (для проверки)
- Пусть N = 4, пары (k,c): (1,10), (1,20), (2,10), (2,20)
- freq_k: {1:2, 2:2} → S_k = C(2,2) + C(2,2) = 1+1 = 2
- freq_c: {10:2, 20:2} → S_c = 2
- freq_kc: each (k,c) appears once → G = 0
- T = 6
- B = S_k - G = 2
- A = S_c - G = 2
- P = T - S_k - S_c + G = 6 - 2 - 2 + 0 = 2
- Denominator = sqrt(2) + sqrt(2) = 2*sqrt(2)
- Numerator = sqrt(0) + sqrt(2) = sqrt(2)
- coef = sqrt(2) / (2*sqrt(2)) = 1/2 = 0.5
- Код должен вывести примерно 0.5. Это можно проверить, запустив программу на этот тест.
Если нужно, могу адаптировать решение под другой язык или привести примеры с разбором конкретных чисел.