DP en árboles - Combinar subárboles
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| CF | Karen & Supermarket | Normal | en el módulo |
Este fue el primer problema en el que vi este truco.
Solución
Para dos vectores y , definimos el vector como el que tiene entradas para cada .
De forma similar al editorial, definimos como el costo mínimo para comprar exactamente bienes del subárbol de si no usamos el cupón de , y definimos como el costo mínimo para comprar exactamente bienes del subárbol de si se nos permite usar el cupón de . Actualizamos con uno de los subárboles hijos de haciendo , y de forma análoga para .
#include <iostream>
#include <vector>
using namespace std;
constexpr int MAX_GOODS = 5000;
constexpr long long INF = 1e18;
int initial[MAX_GOODS + 1];
int discounted[MAX_GOODS + 1];
vector<long long> dp[MAX_GOODS + 1][2];
vector<int> child[MAX_GOODS + 1];
vector<long long> total_min(vector<long long> a, vector<long long> b) {
vector<long long> combined(a.size() + b.size() - 1, INF);
for (int i = 0; i < a.size(); i++) {
for (int j = 0; j < b.size(); j++) {
combined[i + j] = min(combined[i + j], a[i] + b[j]);
}
}
return combined;
}
void process(int g) {
dp[g][0] = {0, initial[g]};
/*
* we have INF for the first element because 0 would mess up merging
* if it WAS 0, then that would mean we didn't buy this one
* yet the algo would still think all the children are eligible for coupons
*/
dp[g][1] = {INF, discounted[g]};
for (int t : child[g]) {
process(t);
dp[g][0] = total_min(dp[g][0], dp[t][0]);
dp[g][1] = total_min(dp[g][1], dp[t][1]);
}
for (int i = 0; i < dp[g][1].size(); i++) {
dp[g][1][i] = min(dp[g][1][i], dp[g][0][i]);
}
}
int main() {
int good_num;
int budget;
cin >> good_num >> budget;
for (int i = 1; i <= good_num; i++) {
cin >> initial[i] >> discounted[i];
discounted[i] = initial[i] - discounted[i];
if (i > 1) {
int prereq;
cin >> prereq;
child[prereq].push_back(i);
}
}
process(1);
for (int i = good_num; i >= 0; i--) {
if (dp[1][1][i] <= budget) {
cout << i << endl;
break;
}
}
}El editorial calcula de forma naive una cota de para el tiempo de ejecución de esta solución. ¡Sin embargo, en realidad corre en !
Complejidad temporal de fusionar subárboles
La complejidad se puede demostrar con el siguiente problema:
Tenemos una lista de unos y un contador inicialmente en . Mientras la lista tenga más de un elemento, quitamos dos elementos cualesquiera y de la lista, sumamos al contador y agregamos a la lista. En términos de , ¿cuál es el valor máximo posible del contador al final de este proceso?
Solución
El contador siempre será igual a al final de este proceso; cada par de unos contribuye uno a la respuesta. Sumar al contador corresponde a fusionar dos subárboles de tamaños y en un subárbol de tamaño en , lo que en total da una complejidad de .
Problemas
| Hecho | Fuente | Nombre | Dificultad | Tags | Solución |
|---|---|---|---|---|---|
| CEOI | 2017 - Museum | Fácil | Solución | ||
| COCI | 2019 - Dzumbus | Normal | Solución | ||
| CF | Cereal Trees II | Normal | Tree, DP | — | |
| IOI | 2005 - Rivers | Normal | Solución | ||
| AC | Tree Patrolling | Normal | DP, Tree | — | |
| CF | Ostap & Tree | Normal | DP | Solución | |
| CF | Div 1 D - Miss Punyverse | Normal | — | ||
| COCI | Periodni | Difícil | NT | Solución |