Problem:


Score : 100 points

Problem Statement

There is a tree with N vertices, numbered 1, 2, \ldots, N.
For each i (1 \leq i \leq N - 1), the i-th edge connects Vertex x_i and y_i.

Taro has decided to paint each vertex in white or black, so that any black vertex can be reached from any other black vertex by passing through only black vertices.

You are given a positive integer M.
For each v (1 \leq v \leq N), answer the following question:

Assuming that Vertex v has to be black, find the number of ways in which the vertices can be painted, modulo M.

Constraints

All values in input are integers.

1 \leq N \leq 10^5

2 \leq M \leq 10^9

1 \leq x_i, y_i \leq N

The given graph is a tree.

Input

Input is given from Standard Input in the following format:

N M
x_1 y_1
x_2 y_2
:
x_{N - 1} y_{N - 1}

Output

Print N lines.
The v-th (1 \leq v \leq N) line should contain the answer to the following question:

Assuming that Vertex v has to be black, find the number of ways in which the vertices can be painted, modulo M.

Sample Input 1

3 100
1 2
2 3

Sample Output 1

3
4
3

There are seven ways to paint the vertices, as shown in the figure below.
Among them, there are three ways such that Vertex 1 is black, four ways such that Vertex 2 is black and three ways such that Vertex 3 is black.

Sample Input 2

4 100
1 2
1 3
1 4

Sample Output 2

8
5
5
5

Sample Input 3

1 100

Sample Output 3

1

Sample Input 4

10 2
8 5
10 8
6 5
1 5
4 8
2 10
3 6
9 2
1 7

Sample Output 4

0
0
1
1
1
0
1
0
1
1

Be sure to print the answers modulo M.



Sample input:
3 100
1 2
2 3


Sample output:
3
4
3

Submission status: Accepted

Solution code:
class Graph:

    def __init__(self, n_vertices, edges, directed=True):
        self.n_vertices = n_vertices
        self.edges = edges
        self.directed = directed

    @property
    def adj(self):
        try:
            return self._adj
        except AttributeError:
            adj = [[] for _ in range(self.n_vertices)]
            if self.directed:
                for u,v in self.edges:
                    adj[u].append(v)
            else:
                for u,v in self.edges:
                    adj[u].append(v)
                    adj[v].append(u)
            self._adj = adj
            return adj

class RootedTree(Graph):
    def __init__(self, n_vertices, edges, root_vertex):
        self.root = root_vertex
        super().__init__(n_vertices, edges, False)

    @property
    def parent(self):
        try:
            return self._parent
        except AttributeError:
            adj = self.adj
            parent = [None]*self.n_vertices
            parent[self.root] = -1
            stack = [self.root]
            for _ in range(self.n_vertices):
                v = stack.pop()
                for u in adj[v]:
                    if parent[u] is None:
                        parent[u] = v
                        stack.append(u)
            self._parent = parent
            return parent

    @property
    def children(self):
        try:
            return self._children
        except AttributeError:
            children = [None]*self.n_vertices
            for v,(l,p) in enumerate(zip(self.adj,self.parent)):
                children[v] = [u for u in l if u != p]
            self._children = children
            return children

    @property
    def dfs_order(self):
        try:
            return self._dfs_order
        except AttributeError:
            order = [None]*self.n_vertices
            children = self.children
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                order[i] = v
                for u in children[v]:
                    stack.append(u)
            self._dfs_order = order
            return order


from functools import reduce
from itertools import accumulate,chain
def rerooting(rooted_tree, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T
 
    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u), v)
    ただし(u,v)は辺
    """
    N = rooted_tree.n_vertices
    parent = rooted_tree.parent
    children = rooted_tree.children
    order = rooted_tree.dfs_order

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in children[v]),
            identity), v)

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        if len(children[v]) == 0:
            continue
        temp = (dp_up[v],)+tuple(dp_down[u] for u in children[v])+(identity,)
        left = accumulate(temp[:-2],merge)
        right = tuple(accumulate(reversed(temp[2:]),merge))
        for u,l,r in zip(children[v],left,reversed(right)):
            dp_up[u] = finalize(merge(l,r), v)

    res = [None]*N
    for v,l in enumerate(children):
        res[v] = reduce(merge,
                    (dp_down[u] for u in children[v]),
                    identity)
        res[v] = finalize(merge(res[v], dp_up[v]), v)
    return res

def solve(N,M,E):

    tree = RootedTree(N,E,0)

    def merge(x,y):
        return (x*y)%M
    def finalize(x,v):
        return x+1

    res = rerooting(tree,merge,1,finalize)
    return [v-1 for v in res]

import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline

if __name__ == '__main__':
    N,M = map(int,readline().split())

    temp = map(lambda x: int(x)-1,read().split())
    E = zip(temp,temp)
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:
class Graph:

    def __init__(self, n_vertices, edges, directed=True):
        self.n_vertices = n_vertices
        self.edges = edges
        self.directed = directed

    @property
    def adj(self):
        try:
            return self._adj
        except AttributeError:
            adj = [[] for _ in range(self.n_vertices)]
            if self.directed:
                for u,v in self.edges:
                    adj[u].append(v)
            else:
                for u,v in self.edges:
                    adj[u].append(v)
                    adj[v].append(u)
            self._adj = adj
            return adj

class RootedTree(Graph):
    def __init__(self, n_vertices, edges, root_vertex):
        self.root = root_vertex
        super().__init__(n_vertices, edges, False)

    @property
    def parent(self):
        try:
            return self._parent
        except AttributeError:
            adj = self.adj
            parent = [None]*self.n_vertices
            parent[self.root] = -1
            stack = [self.root]
            for _ in range(self.n_vertices):
                v = stack.pop()
                for u in adj[v]:
                    if parent[u] is None:
                        parent[u] = v
                        stack.append(u)
            self._parent = parent
            return parent

    @property
    def children(self):
        try:
            return self._children
        except AttributeError:
            children = [None]*self.n_vertices
            for v,(l,p) in enumerate(zip(self.adj,self.parent)):
                children[v] = [u for u in l if u != p]
            self._children = children
            return children

    @property
    def dfs_order(self):
        try:
            return self._dfs_order
        except AttributeError:
            order = [None]*self.n_vertices
            children = self.children
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                order[i] = v
                for u in children[v]:
                    stack.append(u)
            self._dfs_order = order
            return order


from functools import reduce
from itertools import accumulate,chain
def rerooting(rooted_tree, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T
 
    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u), v)
    ただし(u,v)は辺
    """
    N = rooted_tree.n_vertices
    parent = rooted_tree.parent
    children = rooted_tree.children
    order = rooted_tree.dfs_order

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in children[v]),
            identity), v)

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        if len(children[v]) == 0:
            continue
        temp = (dp_up[v],)+tuple(dp_down[u] for u in children[v])+(identity,)
        left = accumulate(temp[:-2],merge)
        right = tuple(accumulate(reversed(temp[2:]),merge))
        for u,l,r in zip(children[v],left,reversed(right)):
            dp_up[u] = finalize(merge(l,r), v)

    res = [None]*N
    for v,l in enumerate(children):
        res[v] = reduce(merge,
                    (dp_down[u] for u in children[v]),
                    identity)
        res[v] = finalize(merge(res[v], dp_up[v]), v)
    return res

def solve(N,M,E):

    tree = RootedTree(N,E,0)

    def merge(x,y):
        return (x*y)%M
    def finalize(x,v):
        return x+1

    res = rerooting(tree,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:
class Graph:
    def __init__(self, n_vertices, edges, directed=True, weighted=False):
        self.n_vertices = n_vertices
        self.edges = edges
        self.directed = directed
        self.weighted = weighted

    @property
    def adj(self):
        try:
            return self._adj
        except AttributeError:
            adj = [[] for _ in range(self.n_vertices)]
            def d_w(e):
                adj[e[0]].append((e[1],e[2]))
            def ud_w(e):
                adj[e[0]].append((e[1],e[2]))
                adj[e[1]].append((e[0],e[2]))
            def d_uw(e):
                adj[e[0]].append(e[1])
            def ud_uw(e):
                adj[e[0]].append(e[1])
                adj[e[1]].append(e[0])
            helper = (ud_uw, d_uw, ud_w, d_w)[self.directed+self.weighted*2]
            for e in self.edges:
                helper(e)
            self._adj = adj
            return adj

class RootedTree(Graph):
    def __init__(self, n_vertices, edges, root_vertex):
        self.root = root_vertex
        super().__init__(n_vertices, edges, False, False)

    @property
    def parent(self):
        try:
            return self._parent
        except AttributeError:
            adj = self.adj
            parent = [None]*self.n_vertices
            parent[self.root] = -1
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                for u in adj[v]:
                    if parent[u] is None:
                        parent[u] = v
                        stack.append(u)
            self._parent = parent
            return parent

    @property
    def children(self):
        try:
            return self._children
        except AttributeError:
            children = [None]*self.n_vertices
            for v,(l,p) in enumerate(zip(self.adj,self.parent)):
                children[v] = [u for u in l if u != p]
            self._children = children
            return children

    @property
    def dfs_order(self):
        try:
            return self._dfs_order
        except AttributeError:
            order = [None]*self.n_vertices
            children = self.children
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                order[i] = v
                for u in children[v]:
                    stack.append(u)
            self._dfs_order = order
            return order

from functools import reduce
from itertools import accumulate,chain
def rerooting(rooted_tree, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T

    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u))
    ただし(u,v)は辺
    """
    N = rooted_tree.n_vertices
    parent = rooted_tree.parent
    children = rooted_tree.children
    order = rooted_tree.dfs_order

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in children[v]),
            identity))

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        if len(children[v]) == 0:
            continue
        temp = (dp_up[v],)+tuple(dp_down[u] for u in children[v])+(identity,)
        left = tuple(accumulate(temp,merge))
        right = tuple(accumulate(reversed(temp[2:]),merge))
        for u,l,r in zip(children[v],left,reversed(right)):
            dp_up[u] = finalize(merge(l,r))

    res = [None]*N
    for v,l in enumerate(children):
        res[v] = reduce(merge,
                    (dp_down[u] for u in children[v]),
                    identity)
        res[v] = finalize(merge(res[v], dp_up[v]))

    return res

def solve(N,M,E):

    tree = RootedTree(N,E,0)

    def merge(x,y):
        return (x*y)%M
    def finalize(x):
        return x+1

    res = rerooting(tree,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:
class Graph:
    def __init__(self, n_vertices, edges, directed=True, weighted=False):
        self.n_vertices = n_vertices
        self.edges = edges
        self.directed = directed
        self.weighted = weighted

    @property
    def adj(self):
        try:
            return self._adj
        except AttributeError:
            adj = [[] for _ in range(self.n_vertices)]
            def d_w(e):
                adj[e[0]].append((e[1],e[2]))
            def ud_w(e):
                adj[e[0]].append((e[1],e[2]))
                adj[e[1]].append((e[0],e[2]))
            def d_uw(e):
                adj[e[0]].append(e[1])
            def ud_uw(e):
                adj[e[0]].append(e[1])
                adj[e[1]].append(e[0])
            helper = (ud_uw, d_uw, ud_w, d_w)[self.directed+self.weighted*2]
            for e in self.edges:
                helper(e)
            self._adj = adj
            return adj

class RootedTree(Graph):
    def __init__(self, n_vertices, edges, root_vertex):
        self.root = root_vertex
        super().__init__(n_vertices, edges, False, False)

    @property
    def parent(self):
        try:
            return self._parent
        except AttributeError:
            adj = self.adj
            parent = [None]*self.n_vertices
            parent[self.root] = -1
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                for u in adj[v]:
                    if parent[u] is None:
                        parent[u] = v
                        stack.append(u)
            self._parent = parent
            return parent

    @property
    def children(self):
        try:
            return self._children
        except AttributeError:
            children = [None]*self.n_vertices
            for v,(l,p) in enumerate(zip(self.adj,self.parent)):
                children[v] = [u for u in l if u != p]
            self._children = children
            return children

    @property
    def dfs_order(self):
        try:
            return self._dfs_order
        except AttributeError:
            order = [None]*self.n_vertices
            children = self.children
            stack = [self.root]
            for i in range(self.n_vertices):
                v = stack.pop()
                order[i] = v
                for u in children[v]:
                    stack.append(u)
            self._dfs_order = order
            return order

from functools import reduce
from itertools import accumulate,chain
def rerooting(rooted_tree, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T

    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u))
    ただし(u,v)は辺
    """
    N = rooted_tree.n_vertices
    parent = rooted_tree.parent
    children = rooted_tree.children
    order = rooted_tree.dfs_order

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in children[v]),
            identity))

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        if len(children[v]) == 0:
            continue
        temp = (dp_up[v],)+tuple(dp_down[u] for u in children[v])+(identity,)
        left = tuple(accumulate(temp,merge))
        right = tuple(accumulate(reversed(temp),merge))
        for i,u in enumerate(children[v]):
            dp_up[u] = finalize(merge(left[i],right[-i-3]))

    res = [None]*N
    for v,l in enumerate(children):
        res[v] = reduce(merge,
                    (dp_down[u] for u in children[v]),
                    identity)
        res[v] = finalize(merge(res[v], dp_up[v]))

    return res

def solve(N,M,E):

    tree = RootedTree(N,E,0)

    def merge(x,y):
        return (x*y)%M
    def finalize(x):
        return x+1

    res = rerooting(tree,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:

from functools import reduce
from itertools import accumulate,chain
def rerooting(N, adj, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T

    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u))
    ただし(u,v)は辺
    """

    order = [None]*N
    parent = [None]*N
    parent[0] = -1
    stack = [0]
    for i in range(N):
        v = stack.pop()
        order[i] = v
        for u in adj[v]:
            if parent[u] is None:
                parent[u] = v
                stack.append(u)

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        p = parent[v]
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in adj[v] if c != p),
            identity))

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        p = parent[v]
        if len(adj[v]) == 1 and adj[v][0] == p:
            continue
        temp = (dp_up[v],)+tuple(dp_down[u] for u in adj[v] if u != p)+(identity,)
        left = tuple(accumulate(temp,merge))
        right = tuple(accumulate(reversed(temp),merge))
        i = 0
        for u in adj[v]:
            if u != p:
                dp_up[u] = finalize(merge(left[i],right[-i-3]))
                i += 1

    for v,l in enumerate(adj):
        order[v] = reduce(merge,
                    (dp_down[u] for u in adj[v] if u != parent[v]),
                    identity)
        order[v] = finalize(merge(order[v], dp_up[v]))

    return order

def solve(N,M,E):

    adj = [[] for _ in range(N)]
    for a,b in E:
        adj[a].append(b)
        adj[b].append(a)

    def merge(x,y):
        return (x*y)%M
    def finalize(x):
        return x+1

    res = rerooting(N,adj,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:

from functools import reduce
from itertools import accumulate,chain
def rerooting(N, adj, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T

    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u))
    ただし(u,v)は辺
    """

    order = [None]*N
    parent = [None]*N
    parent[0] = -1
    stack = [0]
    for i in range(N):
        v = stack.pop()
        order[i] = v
        for u in adj[v]:
            if parent[u] is None:
                parent[u] = v
                stack.append(u)

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        p = parent[v]
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in adj[v] if c != p),
            identity))

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        p = parent[v]
        if len(adj[v]) == 1 and adj[v][0] == p:
            continue
        left = tuple(accumulate(
            chain((dp_up[v],),(dp_down[u] for u in adj[v] if u != p)),
            merge))
        right = tuple(accumulate(
            chain((identity,),(dp_down[u] for u in reversed(adj[v]) if u != p)),
            merge))
        i = 0
        for u in adj[v]:
            if u != p:
                dp_up[u] = finalize(merge(left[i],right[-i-2]))
                i += 1

    for v,l in enumerate(adj):
        order[v] = reduce(merge,
                    (dp_down[u] for u in adj[v] if u != parent[v]),
                    identity)
        order[v] = finalize(merge(order[v], dp_up[v]))

    return order

def solve(N,M,E):

    adj = [[] for _ in range(N)]
    for a,b in E:
        adj[a].append(b)
        adj[b].append(a)

    def merge(x,y):
        return (x*y)%M
    def finalize(x):
        return x+1

    res = rerooting(N,adj,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:

from functools import reduce
def rerooting(N, adj, merge, identity, finalize):
    """
    merge: (T,T) -> T, (T, merge)はモノイド
    identity: 単位元
    finalize: T -> T

    以下の形で書けるdpは対応可能
    dp[u,v] = finalize(merge(dp[v,k] for k in adj[v] if k != u))
    ただし(u,v)は辺
    """

    order = [None]*N
    parent = [None]*N
    parent[0] = -1
    stack = [0]
    for i in range(N):
        v = stack.pop()
        order[i] = v
        for u in adj[v]:
            if parent[u] is None:
                parent[u] = v
                stack.append(u)

    # from leaf to parent
    dp_down = [None]*N
    for v in reversed(order[1:]):
        p = parent[v]
        dp_down[v] = finalize(reduce(merge,
            (dp_down[c] for c in adj[v] if c != p),
            identity))

    # from parent to leaf
    dp_up = [None]*N
    dp_up[0] = identity
    for v in order:
        if len(adj[v]) == 1 and adj[v][0] == parent[v]:
            continue
        p = parent[v]
        left = [dp_up[v]]
        for u in adj[v]:
            if u != p:
                left.append(merge(left[-1],dp_down[u]))

        right = [identity]
        for u in reversed(adj[v]):
            if u != p:
                right.append(merge(dp_down[u],right[-1]))
        i = 0
        for u in adj[v]:
            if u != p:
                dp_up[u] = finalize(merge(left[i],right[-i-2]))
                i += 1

    for v,l in enumerate(adj):
        order[v] = reduce(merge,
                    (dp_down[u] for u in adj[v] if u != parent[v]),
                    identity)
        order[v] = finalize(merge(order[v], dp_up[v]))

    return order

def solve(N,M,E):

    adj = [[] for _ in range(N)]
    for a,b in E:
        adj[a].append(b)
        adj[b].append(a)

    def merge(x,y):
        return (x*y)%M
    def finalize(x):
        return x+1

    res = rerooting(N,adj,merge,1,finalize)
    return [v-1 for v in res]


if __name__ == '__main__':
    N,M = map(int,input().split())
    E = [tuple(map(lambda x: int(x)-1, input().split())) for _ in range(N-1)]
    print(*solve(N,M,E),sep='\n')

Submission status: Accepted

Solution code:
from collections import defaultdict
import sys
input=sys.stdin.readline


class Reroot:
    def __init__(self, N, e, merge, f, g):
        self.G = defaultdict(list)
        self.parents = [-1]*(N+1)
        self.dp = defaultdict(lambda: defaultdict(int))
        self.ans = [0]*(N+1)
        self.merge = merge
        self.f = f
        self.g = g
        self.e = e

    def add_edge(self, s, t):
        self.G[s].append(t)

    def build(self):
        self.dfs1(1)
        self.dfs2(1)
        return self.ans

    def dfs1(self, s):
        l = []
        q = [1]
        while q:
            s = q.pop()
            l.append(s)
            for t in self.G[s]:
                if t == self.parents[s]:
                    continue
                self.parents[t] = s
                q.append(t)

        for s in l[::-1]:
            v = self.e
            for t in self.G[s]:
                if t == self.parents[s]:
                    continue
                v = self.merge(v, self.f(self.dp[s][t], 1))
            self.dp[self.parents[s]][s] = self.g(v, 0)

    def dfs2(self, s):
        q = [(s, self.e)]
        while q:
            s, dp_p = q.pop()
            par = self.parents[s]
            for t in self.G[s]:
                if t == par:
                    self.dp[s][t] = dp_p

            size = len(self.G[s])
            ac_l = [self.e] * (size+1)
            ac_r = [self.e] * (size+1)
            for i, t in enumerate(self.G[s]):
                ac_l[i+1] = self.merge(ac_l[i], self.f(self.dp[s][t], 1))
            for i, t in enumerate(self.G[s][::-1]):
                ac_r[size-i-1] = self.merge(ac_r[size-i], self.f(self.dp[s][t], 1))
            self.ans[s] = self.g(ac_l[size], 0)
            for i, t in enumerate(self.G[s]):
                if t == par:
                    continue
                q.append((t, self.g(self.merge(ac_l[i], ac_r[i+1]), 0)))


def solve():
    N, M = map(int, input().split())
    
    merge = lambda x,y:(x*y)%M
    f = lambda x,y:x+y
    g = lambda x,y:x+y
    e = 1
    tree = Reroot(N, e, merge, f, g)

    for _ in range(N-1):
        a, b = map(int, input().split())
        tree.add_edge(a, b)
        tree.add_edge(b, a)
    ans = tree.build()
    for i in range(1, N+1):
        print(ans[i])


if __name__ == "__main__":
    solve()

Submission status: Accepted

Solution code:
from collections import defaultdict
from operator import add, mul
import sys
input=sys.stdin.readline


class Reroot:
    def __init__(self, N, M):
        self.G = defaultdict(list)
        self.parents = [-1]*(N+1)
        self.dp = defaultdict(lambda: defaultdict(int))
        self.ans = [0]*(N+1)
        self.merge = lambda x,y:mul(x, y)
        self.add_root = lambda x:x#+1
        self.e = 1
        self.M = M

    def add_edge(self, s, t):
        self.G[s].append(t)

    def build(self):
        self.dfs1(1)
        self.dfs2(1)
        return self.ans

    def dfs1(self, s):
        l = []
        q = [1]
        while q:
            s = q.pop()
            l.append(s)
            for t in self.G[s]:
                if t == self.parents[s]:
                    continue
                self.parents[t] = s
                q.append(t)

        for s in l[::-1]:
            v = self.e
            for t in self.G[s]:
                if t == self.parents[s]:
                    continue
                v = self.merge(v, self.dp[s][t]+1)
            self.dp[self.parents[s]][s] = self.add_root(v)

    def dfs2(self, s):
        q = [(s, self.e)]
        while q:
            s, dp_p = q.pop()
            par = self.parents[s]
            for t in self.G[s]:
                if t == par:
                    self.dp[s][t] = dp_p

            size = len(self.G[s])
            ac_l = [self.e] * (size+1)
            ac_r = [self.e] * (size+1)
            for i, t in enumerate(self.G[s]):
                ac_l[i+1] = self.merge(ac_l[i], self.dp[s][t]+1)%self.M
            for i, t in enumerate(self.G[s][::-1]):
                ac_r[size-i-1] = self.merge(ac_r[size-i], self.dp[s][t]+1)%self.M
            self.ans[s] = self.add_root(ac_l[size])
            for i, t in enumerate(self.G[s]):
                if t == par:
                    continue
                q.append((t, self.add_root(self.merge(ac_l[i], ac_r[i+1])%self.M)))


def solve():
    N, M = map(int, input().split())
    tree = Reroot(N, M)
    for _ in range(N-1):
        a, b = map(int, input().split())
        tree.add_edge(a, b)
        tree.add_edge(b, a)
    ans = tree.build()
    for i in range(1, N+1):
        print(ans[i])


if __name__ == "__main__":
    solve()

Submission status: Accepted

Solution code:
from collections import defaultdict
from operator import add, mul
import sys
sys.setrecursionlimit(1000000)


d = defaultdict(list)
N, M = map(int, input().split())
for _ in range(N-1):
    a, b = map(int, input().split())
    d[a].append(b)
    d[b].append(a)

q = [1]
parents = [-1]*(N+1)
dp = defaultdict(lambda: defaultdict(int))
ans = [0]*(N+1)
merge = lambda x,y:mul(x, y)
add_root = lambda x:x#+1
e = 1


def dfs1(s):
    l = []
    q = [1]
    while q:
        s = q.pop()
        l.append(s)
        for t in d[s]:
            if t==parents[s]:
                continue
            parents[t] = s
            q.append(t)

    for s in l[::-1]:
        v = e
        for t in d[s]:
            if t == parents[s]:
                continue
            v = merge(v, dp[s][t]+1)
        dp[parents[s]][s] = add_root(v)
    # return add_root(v)


def dfs2(s):
    q = [(s, e)]
    while q:
        s, dp_p = q.pop()
        par = parents[s]
        for t in d[s]:
            if t == par:
                dp[s][t] = dp_p

        size = len(d[s])
        ac_l = [e] * (size+1)
        ac_r = [e] * (size+1)
        for i, t in enumerate(d[s]):
            ac_l[i+1] = merge(ac_l[i], dp[s][t]+1)%M
        for i, t in enumerate(d[s][::-1]):
            ac_r[size-i-1] = merge(ac_r[size-i], dp[s][t]+1)%M
        ans[s] = add_root(ac_l[size])
        for i, t in enumerate(d[s]):
            if t == par:
                continue
            q.append((t, add_root(merge(ac_l[i], ac_r[i+1])%M)))


dfs1(1)
dfs2(1)
for i in range(1, N+1):
    print(ans[i])