Yet Another Tournament
Explicación
Primero, observemos que los resultados entre oponentes se conocen de antemano: el oponente siempre vence al oponente si , así que el oponente tiene exactamente victorias de los partidos entre oponentes, independientemente de lo que hagas.
Cuando juegas contra el oponente :
- Si lo vences: termina con victorias.
- Si pierdes contra él: termina con victorias (gana una sobre ti).
Contar oponentes que te vencen
Si vences a un subconjunto de oponentes (con ), tienes victorias. Los oponentes con índice siempre terminan con más victorias que tú. El único oponente en el límite es el oponente : tiene exactamente victorias de otros partidos, y te vence si perdiste contra él, lo que lo pone por encima de ti. Así, vencer al oponente te ahorra exactamente un puesto.
Estrategia óptima
Iteramos sobre cada válido (número de oponentes que vences) y calculamos el mejor puesto alcanzable:
-
Caso 1: Vencer cualesquiera oponentes dentro del presupuesto . De forma voraz tomamos los oponentes más baratos y comprobamos la factibilidad con sumas de prefijos sobre los costos ordenados. Esto da el puesto .
-
Caso 2: Vencer oponentes, incluyendo específicamente al oponente , dentro del presupuesto . Sacamos al oponente de consideración, tomamos los oponentes restantes más baratos, y sumamos al costo. Si esto cabe en , el puesto mejora a .
Tomamos el puesto mínimo sobre todos los válidos.
Implementación
Complejidad temporal:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) { cin >> a[i]; }
vector<int> sorted_a(a.begin() + 1, a.end());
sort(sorted_a.begin(), sorted_a.end());
// prefix[i] = suma de los i oponentes más baratos
vector<int> prefix(n + 1);
for (int i = 0; i < n; i++) { prefix[i + 1] = prefix[i] + sorted_a[i]; }
int ans = n + 1;
for (int k = 0; k <= n; k++) {
if (prefix[k] > m) break;
// caso 1: si podemos vencer cualesquiera k oponentes, nuestro puesto es al menos n - k + 1.
ans = min(ans, n - k + 1);
// caso 2: si también podemos vencer al oponente k+1, nuestro puesto mejora a n - k.
if (k + 1 > n) continue;
int cost_with;
if (k == 0) {
cost_with = a[k + 1];
} else {
// los k-1 más baratos tras quitar al oponente k+1, más a[k+1]
int excl;
if (a[k + 1] <= sorted_a[k - 1]) {
excl = prefix[k] - a[k + 1];
} else {
excl = prefix[k - 1];
}
cost_with = excl + a[k + 1];
}
if (cost_with <= m) { ans = min(ans, n - k); }
}
cout << ans << '\n';
}
}