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(N2logN)\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+AjKA_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+Aij \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+AjKA_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 1lj1 \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+Aij\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 benefitK(countM)\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(countM)K\cdot (\texttt{count}-M), ya que no deberíamos tomar countM\texttt{count}-M apretones de mano de valor exactamente KK.

Implementación

Complejidad temporal: O(NlogN)\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} }