Editorial for Hange y los árboles


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Author: humbertoyusta

Dado el recorrido en preorden de un árbol binario, se pueden analizar todas las formas de construirlo de la siguiente forma: se selecciona un nodo x, se hace raíz del árbol, y todos los nodos que estén antes de x en el recorrido formarán parte del subárbol izquierdo, y el resto del derecho, así que podemos definir una función f(l, r) que cuente la cantidad de árboles posibles que se pueden construir de l a r, que seleccione cada elemento x, lo tome como raíz del árbol, y sume f(l, x-1) y f(x+1, r), además debido a la restricción del problema el valor la raíz tiene que ser igual al mínimo valor del rango. Con esto se puede hacer una solución con dp en O(n^3).

Debido a que cada nodo debe tener un valor mayor o igual al de su padre, se puede separar el problema en los elementos de valor 1, los de valor 2, etc.

Para los elementos de un valor x, la cantidad de árboles que se pueden formar con ellos es igual a la cantidad total de árboles binarios de cnt_x nodos, que se puede calcular como Catalan_n, para más información vea el artículo de wikipedia sobre ello, Catalan_n se puede calcular como C^{2n}_{n} - C^{2n}_{n+1}.

Ahora entre las aristas que formamos con los nodos de valor menor o igual a x tenemos que contar los árboles de los elementos hasta x + 1, cada rango que tenga todos los nodos con un valor \geq x+1, el padre de su raíz podrá ser conectado solo del nodo de valor mayor más cerca a su izquierda, o a su derecha, pero no de ambos, por lo cual podemos ejecutar un algoritmo divide and conquer, que seleccione todos los mínimos, calcule el producto de todos los rangos de elementos mayores que este, y multiplique la respuesta por \(Catalan_{frecuencia_{mínimo}}\).

Se puede programar en O(n^2) con la forma intuitiva de calcular los catalanes, y buscando los mínimos con un simple ciclo, en O(n\log{n}) con un segment tree para buscar los mínimos, y calculando los catalanes con la fórmula a partir de las combinaciones, e incluso en O(n) usando stacks monótonas(como hizo mdario en el contest) y precalculando los inversos de los factoriales en tiempo lineal.

Para más detalle puede consultar el código siguiente:

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 6, MOD = 1e9 + 7;
int tree[1 << 21], a[N], n, fact[1 << 21], frev[1 << 21];

void build(int b = 0, int e = n, int idx = 1) {
    if (b + 1 > e) return;
    if (b + 1 == e) {
        tree[idx] = b;
        return;
    }
    int m = (b + e) / 2;
    build(b, m, idx * 2 + 0);
    build(m, e, idx * 2 + 1);
    if (a[tree[idx * 2 + 0]] <= a[tree[idx * 2 + 1]]) {
        tree[idx] = tree[idx * 2 + 0];
    } else {
        tree[idx] = tree[idx * 2 + 1];
    }
}

int query(int l, int r, int b = 0, int e = n, int idx = 1) {
    if (l == b && r == e) {
        return tree[idx];
    }
    int m = (b + e) / 2, L = -1, R = -1;
    if (l < m) {
        L = query(l, min(m, r), b, m, idx * 2 + 0);
    }
    if (r > m) {
        R = query(max(m, l), r, m, e, idx * 2 + 1);
    }
    if (L == -1) return R;
    if (R == -1) return L;
    if (a[L] <= a[R]) {
        return L;
    } else {
        return R;
    }
}

int power(int a, int n) {
    if (n == 0) return 1;
    return power(1LL * a * a % MOD, n / 2) * (n & 1 ? 1LL * a : 1LL) % MOD;
}

int choose(int n, int k) {
    return (1LL * fact[n] * frev[k] % MOD) * frev[n - k] % MOD;
}

int catalan(int x) {
    return (choose(2 * x, x) - choose(2 * x, x + 1) + MOD) % MOD;
}

int solve(int l, int r) {
    if (l + 1 >= r) return 1;
    int p = query(l, r);
    int q = p + 1, cnt = 1, ans = solve(l, p);
    while (q < r && a[query(q, r)] == a[p]) { // TODO: change this
        int lastQ = q;
        q = query(q, r) + 1;
        ans = (1LL * ans * solve(lastQ, q - 1)) % MOD;
        ++cnt;
    }
    ans = 1LL * ans * solve(q, r) % MOD;
    ans = 1LL * ans * catalan(cnt) % MOD;
    return ans;
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    fact[0] = frev[0] = 1;
    for (int i = 1; i < (1 << 21); ++i) {
        fact[i] = 1LL * i * fact[i - 1] % MOD;
        frev[i] = power(fact[i], MOD - 2);
    }
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    build();
    cout << solve(0, n) << endl;
}

Comments

There are no comments at the moment.