You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

81 lines
4.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = N << 1;
#define int long long
#define endl "\n"
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int f[N]; // 在以1为全局根的情况下f[i]记录以i为根的子树并且把i染成黑色时i为根的子树中所有可能的染色方案数量
int g[N];
int mod; // 对 mod 值取模
int pre[N]; // pre[i]:记录i节点的前缀积对mod取模后的值
int suff[N]; // suff[i]:记录i节点的后缀和对mod取模后的值
// 先递归再统计以子填父用儿子们的贡献更新父亲的值。最底层儿子也就是叶子的贡献值是1,也就是只把它染成黑色,对于这个叶子的父亲而言,
// 它统计方案时它认为这个儿子提供的方案数是2因为儿子也可以不染色也就是白色。当然儿子也可以染成黑色所以它理解为2。
void dfs1(int u, int fa) {
f[u] = 1; // 以u为根的子树不管它是不是有子孙节点最起码可以把u染成黑色这样就可以有1种方案
vector<int> son; // 记录u有哪些儿子方便后的计算。不使用链式前向星直接枚举的原因在于前向星只能正序枚举
// 本题目中还需要倒序枚举,前向星记录的是单链表,不是双链表,无法倒序枚举,只能是跑一遍,记录下来,然后再倒着枚举
// 如此在本题中链式前向星就不如邻接表来的快邻接表就是可以for(int i=0;i<edge[i].size();i++),也可以for(int i=edge[i].size()-1;i>=0;i--)
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa) continue;
dfs1(v, u);
f[u] = f[u] * (f[v] + 1) % mod; // 全白也是一种方案对于v子树而言它并不是只能提供以v染成黑色的所有方案还有一种v没有染成黑色的方案数。
// 而此时由于v没有染成黑色所以v子树就没有进去染色也就是整个v子树全都是白色这算是一种特殊的染色方案也就是啥都不染。
// 所以 f[v]+1 就是v子树的所有贡献值,而f[u]需要把自己所有儿子的共献值累乘起来才是自己的贡献值。
son.push_back(v); // 将子节点加入集合,方便之后操作
}
// 记录前缀积
int t1 = 1; // 前缀积取模后的值
for (int i = 0; i < son.size(); i++) { // 将儿子数组正着枚举
// 利用静态数组pre,记录每个节点的前缀积取模后的值
pre[son[i]] = t1; // 到我以前,所有结果的累乘积是多少
t1 = t1 * (f[son[i]] + 1) % mod; // 我完成后,需要把我的贡献也乘到累乘积中,以便我的下一个节点计算它的累乘积时使用
}
// 记录后缀积
int t2 = 1; // 后缀积取模后的值
for (int i = son.size() - 1; i >= 0; i--) { // 将儿子数组倒着枚举
// 利用静态数组suff,记录每个节点的后缀积取模后的值
suff[son[i]] = t2; // 到我以前,所有结果的累乘积是多少
t2 = t2 * (f[son[i]] + 1) % mod; // 我完成后,需要把我的贡献也乘到累乘积中,以便我的下一个节点计算它的累乘积时使用
}
}
void dfs2(int u, int fa) {
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa) continue;
// g[u] * (pre[v] * suff[v])解析:
// u给v带来的影响不光是u为根的子树这些叶子的贡献还有重要的一部份就是u的fa[u]及其父、叔叔、大爷、爷爷、二爷爷、老太爷、二老太爷来来的方案数
// 这些由父系带来的方案数汇聚到g[u]里
g[v] = (g[u] * (pre[v] * suff[v] % mod) % mod + 1) % mod;
dfs2(v, u);
}
}
signed main() {
// 初始化链式前向星
memset(h, -1, sizeof h);
int n;
cin >> n >> mod;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs1(1, 0);
g[1] = 1; // 考虑出发时所有儿子的贡献都没有交上来那么初化值是1否则连乘积没法乘上来
dfs2(1, 0);
for (int i = 1; i <= n; i++) cout << f[i] * g[i] % mod << endl;
}