Skip to Content

Handshake

Análisis oficial 

Explicación

Es más fácil resolver este problema considerando primero una solución cuadrática. Para resolverlo en O(N2log⁡N)\mathcal{O}(N^2\log{N}), podemos hallar todos los valores posibles de apretones de mano, ordenarlos y tomar los MM mayores.

Para optimizar esta solución, podemos usar búsqueda binaria. Hacemos búsqueda binaria sobre el valor KK tal que nuestros MM apretones de mano más grandes tienen valores ≥K\geq K y los restantes tienen valores ≤K\leq K.

Para esto, ordenamos AA en orden no creciente y construimos un arreglo de sumas de prefijos, pref\texttt{pref}. Luego, para cada valor de KK que comprobamos con búsqueda binaria, usamos dos punteros para calcular el benefit\texttt{benefit} y el count\texttt{count} totales. Para cada índice ii, hallamos el último índice jj tal que Ai+Aj≥KA_i + A_j \geq K. Al decrementar ii en 11, nunca decrementamos jj, lo que permite un recorrido en O(N)\mathcal{O}(N).

benefit=benefit+prefj+Ai⋅j \texttt{benefit}=\texttt{benefit} +\texttt{pref}_j+A_i\cdot j count=count+j \texttt{count}=\texttt{count} + j

Nuestra adición a count\texttt{count} se obtiene del hecho de que si jj es el último índice tal que Ai+Aj≥KA_i + A_j \geq K, podemos emparejar a la persona del índice ii con las primeras jj personas.

Nuestra adición a benefit\texttt{benefit} se obtiene del hecho de que cada uno de los jj apretones de mano, (l,i)(l, i), contribuye Al+AiA_l+A_i exactamente una vez para todo ll con 1≤l≤j1 \leq l \leq j. Esto da la fórmula benefit=benefit+∑l=1j(Al+Ai)\texttt{benefit}=\texttt{benefit} +\sum_{l=1}^{j} (A_l+A_i), que es equivalente a benefit=benefit+∑l=1jAl+Ai⋅j\texttt{benefit}=\texttt{benefit} +\sum_{l=1}^{j} A_l+A_i\cdot j. Podemos usar sumas de prefijos para hallar ∑l=1jAl\sum_{l=1}^{j} A_l en O(1)\mathcal{O}(1), lo que da nuestra fórmula.

Para el KK óptimo, el resultado es benefit−K⋅(count−M)\texttt{benefit}-K\cdot (\texttt{count}-M). Esto es porque, para el resultado óptimo, los apretones de mano con valor exactamente KK se pueden tomar o no. Cuando hallamos el KK óptimo, benefit\texttt{benefit} incluirá todos los apretones de mano de valor KK. Para ajustar este sobreconteo, restamos K⋅(count−M)K\cdot (\texttt{count}-M), ya que no deberíamos tomar count−M\texttt{count}-M apretones de mano de valor exactamente KK.

Implementación

Complejidad temporal: O(Nlog⁡N)\mathcal{O}(N\log{N})

#include <bits/stdc++.h> using namespace std; using ll = long long; const int MAXV = 200000; int main() { int n; ll m; cin >> n >> m; vector<int> a(n); vector<ll> pref(n + 1, 0); for (int &x : a) { std::cin >> x; } sort(a.rbegin(), a.rend()); for (int i = 0; i < n; i++) { pref[i + 1] = a[i] + pref[i]; } int lo = 0, hi = MAXV; ll res = 0; while (lo <= hi) { ll count = 0, benefit = 0; int k = lo + (hi - lo) / 2; int j = 0; for (int i = n - 1; i >= 0; i--) { while (j < n && a[i] + a[j] >= k) { j++; } count += j; benefit += 1LL * j * a[i] + pref[j]; } if (count >= m) { lo = k + 1; res = benefit - (count - m) * k; } else { hi = k - 1; } } cout << res << '\n'; return 0; }
import java.io.*; import java.util.*; public class Main { private static int MAXV = 200000; public static void main(String[] args) throws Exception { Kattio io = new Kattio(); int n = io.nextInt(); long m = io.nextLong(); Integer[] a = new Integer[n]; for (int i = 0; i < n; i++) { a[i] = io.nextInt(); } Arrays.sort(a, Collections.reverseOrder()); long[] pref = new long[n + 1]; for (int i = 0; i < n; i++) { pref[i + 1] = a[i] + pref[i]; } int lo = 0, hi = MAXV; long res = 0; while (lo <= hi) { long count = 0, benefit = 0; int k = lo + (hi - lo) / 2; int j = 0; for (int i = n - 1; i >= 0; i--) { while (j < n && a[i] + a[j] >= k) { j++; } count += j; benefit += (long)j * a[i] + pref[j]; } if (count >= m) { lo = k + 1; res = benefit - (count - m) * k; } else { hi = k - 1; } } io.println(res); io.close(); } // BeginCodeSnip{Kattio} }