解法. 全方位木DP
解法が分からなかったのでkmjpさんとsnukeさんの解説を参考にしました.
を, の親を とした根付き木で を含み を根とする部分木の数 として動的計画法を行います.頂点 の隣接頂点全体を とすると遷移は,
(☆)
となります. となっているのは,頂点 を含まない空の部分木の数も考慮しているからです.もし,各辺 に対して, と が計算できているとすると各頂点 に対する答えは
となります.この出力は 時間でできるので,ここからは と を 時間で求めることを目標に説明していきます.
適当な頂点 を選び, を根とする順序木 を考えます.順序木とは根付き木で各頂点 の子全体 に全順序 を与えたものです. から深さ優先探索で上の漸化式(☆)を計算することによって親 からその子 への値 を 時間で求めることができます.しかし逆向きの は計算されていないためにすべての頂点の答えを計算することはできません.下の図では赤色と青色の矢印が計算したいもので,実線の赤色矢印は計算されていますが青色の点線矢印はまだ計算されていません.
各頂点を根として同様の計算をすることによって青色の矢印の値を計算できますが 時間となり間に合いません.ここで, を計算することを考えます.漸化式(☆)から,
となります.右辺の項はすでに計算されているために 回の積で求めることができます.一般的に頂点 へ向かう青色の矢印は子 に対して,
と計算できます.各 は赤色の矢印ですでに計算されているのですが, へ向かうすべての青色の矢印を愚直に計算すると 回の積が必要となります.したがって全体で 時間かかってしまうのでこの部分を高速にする必要があります.
頂点 の子を とします.ここで任意の に対して,
,
とします.ただし, と の値は 1 とします.これを前処理で 時間で求めることによって,各子 に対して,
と定数時間で求めることができます.したがって, へ向かうすべての青色の矢印を 時間で求めることができるので,全体で 時間で答えを計算することができます.下の図は の計算を表しています.
計算時間:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int N, MOD;
vector<vector<int>> adj;
vector<vector<ll>> dp;
ll Dfs(const int p, const int v) {
ll res = 1;
for (size_t i = 0; i < adj[v].size(); ++i) {
if (adj[v][i] != p) {
dp[v][i] = Dfs(v, adj[v][i]);
(res *= (dp[v][i] + 1)) %= MOD;
}
}
return res;
}
void Rev(const int p, const int v, const ll add) {
for (size_t i = 0; i < adj[v].size(); ++i)
if (adj[v][i] == p) {
dp[v][i] = add;
break;
}
const int deg = adj[v].size();
vector<ll> l(deg), r(deg);
for (int i = 0; i < deg; ++i) r[i] = ((l[i] = (dp[v][i] + 1)) %= MOD);
for (int i = 1; i < deg; ++i) (l[i] *= l[i - 1]) %= MOD;
for (int i = deg - 2; 0 <= i; --i) (r[i] *= r[i + 1]) %= MOD;
for (int i = 0; i < deg; ++i) {
if (adj[v][i] == p) continue;
ll add_c = 1;
if (i != 0) (add_c *= l[i - 1]) %= MOD;
if (i != deg - 1) (add_c *= r[i + 1]) %= MOD;
Rev(v, adj[v][i], add_c);
}
}
int main() {
cin.tie(0); ios::sync_with_stdio(false);
cin >> N >> MOD;
adj.resize(N);
for (int i = 0, x, y; i < N - 1; ++i) {
cin >> x >> y;
adj[x - 1].push_back(y - 1);
adj[y - 1].push_back(x - 1);
}
dp.resize(N);
for (int v = 0; v < N; ++v) dp[v].resize(adj[v].size());
Dfs(-1, 0);
Rev(-1, 0, 0);
for (int v = 0; v < N; ++v) {
ll ans = 1;
for (ll add : dp[v]) (ans *= (add + 1)) %= MOD;
cout << ans << '\n';
}
return 0;
}