Subtree
Resolver para una raíz
Consideremos un problema más simple:
Suponiendo que el nodo está pintado de negro, ¿de cuántas formas podemos pintar el árbol?
Primero, enraizamos el árbol en el nodo . Sea la cantidad de formas en que podemos pintar el subárbol del nodo de modo que o bien el nodo esté coloreado de negro, o bien ningún nodo esté coloreado de negro. Notamos que si es una hoja, entonces (elegimos colorear el nodo de negro o no).
Para cada hijo de , hay formas de pintar su subárbol si está pintado de negro. Esto significa que tenemos la recurrencia
donde el producto corresponde a pintar el nodo de negro y el corresponde a pintar el nodo de blanco.
La respuesta al problema más simple es entonces . Hallar todos los toma tiempo .
Resolver para todas las raíces
Primero, enraizamos el árbol de forma arbitraria y hacemos un DFS para hallar todos los .
Sea la cantidad de formas de colorear el árbol si quitamos el subárbol del nodo de modo que o bien el padre de es negro, o bien ningún nodo está coloreado de negro. Notamos que .
La cantidad de formas de pintar el árbol si sabemos que el nodo es negro es simplemente . ¿Cómo podemos hallar de forma eficiente?
La recurrencia básica para calcular es
donde el producto corresponde a pintar el padre de de negro y el corresponde a pintar el padre de de blanco.
Como no está garantizado que sea primo, no podemos simplemente hallar el producto de los hijos de cada nodo y dividir ese producto por el de cada hijo (porque no podemos hallar inversos modulares con facilidad).
Sin embargo, notamos que si el nodo es el -ésimo hijo de su padre, entonces podemos usar productos de prefijos y sufijos para calcular
sin usar división. (Es decir, hallamos el producto de para el primero hasta el -ésimo hijo del padre de , el producto de para el -ésimo hasta el último hijo del padre de , y luego multiplicamos esos juntos.)
Hallar todos los toma tiempo usando un DFS, así que la complejidad total de este algoritmo es .
Implementación
down corresponde a y up corresponde a . El código usa las
mismas recurrencias mencionadas arriba.
// CodeSnip{Benq Template}
/**
* Description: modular arithmetic operations
*/
template <int RT> struct mint {
// static const int mod = MOD;
static constexpr mint rt() { return RT; } // primitive root for FFT
int v;
explicit operator int() const {
return v;
} // explicit -> don't silently convert to int
mint() { v = 0; }
mint(ll _v) {
v = int((-MOD < _v && _v < MOD) ? _v : _v % MOD);
if (v < 0) v += MOD;
}
friend bool operator==(const mint &a, const mint &b) { return a.v == b.v; }
friend bool operator!=(const mint &a, const mint &b) { return !(a == b); }
friend bool operator<(const mint &a, const mint &b) { return a.v < b.v; }
friend void re(mint &a) {
ll x;
re(x);
a = mint(x);
}
friend str ts(mint a) { return ts(a.v); }
mint &operator+=(const mint &m) {
if ((v += m.v) >= MOD) v -= MOD;
return *this;
}
mint &operator-=(const mint &m) {
if ((v -= m.v) < 0) v += MOD;
return *this;
}
mint &operator*=(const mint &m) {
v = (ll)v * m.v % MOD;
return *this;
}
mint &operator/=(const mint &m) { return (*this) *= inv(m); }
friend mint pow(mint a, ll p) {
mint ans = 1;
assert(p >= 0);
for (; p; p /= 2, a *= a)
if (p & 1) ans *= a;
return ans;
}
friend mint inv(const mint &a) {
assert(a.v != 0);
return pow(a, MOD - 2);
}
mint operator-() const { return mint(-v); }
mint &operator++() { return *this += 1; }
mint &operator--() { return *this -= 1; }
friend mint operator+(mint a, const mint &b) { return a += b; }
friend mint operator-(mint a, const mint &b) { return a -= b; }
friend mint operator*(mint a, const mint &b) { return a *= b; }
friend mint operator/(mint a, const mint &b) { return a /= b; }
};
typedef mint<5> mi;
template <int SZ> struct SubtreeDP {
int par[SZ];
vi adj[SZ];
void ae(int a, int b) { adj[a].pb(b), adj[b].pb(a); }
struct T {
mi v = 1;
T &operator+=(const T &b) {
v *= b.v;
return *this;
}
void tran() { ++v; }
};
T up[SZ], down[SZ];
void dfs(int x) {
trav(t, adj[x]) if (t != par[x]) {
par[t] = x;
dfs(t);
down[x] += down[t];
}
down[x].tran();
}
void dfs2(int x) {
{
T pre = up[x]; // deal with prefixes
F0R(i, sz(adj[x])) {
int c = adj[x][i];
if (c == par[x]) continue;
up[c] += pre;
pre += down[c];
}
}
{
T pre; // deal with suffixes
R0F(i, sz(adj[x])) {
int c = adj[x][i];
if (c == par[x]) continue;
up[c] += pre;
pre += down[c];
}
}
F0R(i, sz(adj[x])) {
int c = adj[x][i];
if (c == par[x]) continue;
up[c].tran();
dfs2(c);
}
}
// T getSub(int x, int y) { return par[x] == y ? down[x] : up[y]; }
// get subtree of x excluding y
void init(int n) {
par[1] = 0;
dfs(1);
dfs2(1);
FOR(i, 1, n + 1) ps((down[i].v - 1) * up[i].v);
// FOR(i,1,n+1) { alternative method
// T p = T(); trav(t,adj[i]) p += getSub(t,i);
// ps(p.v);
// }
}
};
int main() {
setIO();
int n;
re(n, MOD);
SubtreeDP<MX> S;
F0R(i, n - 1) {
int a, b;
re(a, b);
S.ae(a, b);
}
S.init(n);
}