Python Hard
```python3 []
class UnionFind:

    def __init__(self, N):
        self.count = N              
        self.parent = [i for i in range(N)]
        self.rank = [1] * N
        
        
    def find(self, p):
        if p != self.parent[p]:
            self.parent[p] = self.find(self.parent[p]) 
        return self.parent[p]

    def union(self, p, q):
        prt, qrt = self.find(p), self.find(q)
        if prt == qrt: return False
        if self.rank[prt] >= self.rank[qrt]: 
            self.parent[qrt] = prt
            self.rank[prt] += self.rank[qrt]
        else:
            self.parent[prt] = qrt
            self.rank[qrt] += self.rank[prt]
            
        self.count -= 1 
        return True 

class Solution:
    def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:
        '''
        2 ** n works
        '''
        N = n

        for i in range(len(edges)):
            edges[i][0] -= 1
            edges[i][1] -= 1

        # print(edges)

        A = [0] * N


        def bfs_farthest_node(start, graph):
            
            visited = [False] * n
            dist = [0] * n
            queue = deque([start])
            visited[start] = True
            farthest_node = start

            while queue:
                node = queue.popleft()
                for neighbor in graph[node]:
                    if not visited[neighbor]:
                        visited[neighbor] = True
                        dist[neighbor] = dist[node] + 1
                        queue.append(neighbor)
                        if dist[neighbor] > dist[farthest_node]:
                            farthest_node = neighbor

            return farthest_node, dist[farthest_node]

        def find_tree_diameter(e):

           
            seen = set()

            graph = defaultdict(list)
            for u, v in e:
                graph[u].append(v)
                graph[v].append(u)
                seen.add(u)
              

            


            # Start from any node (Node 0 in this case)
            for i in range(n):
                if i in seen:
                    farthest_node, _ = bfs_farthest_node(i, graph)

                    # Find the farthest node from the previously found farthest node
                    farthest_node, diameter = bfs_farthest_node(farthest_node, graph)

                    # print(graph, diameter)
    
                    return diameter
            



        self.seen = set()
        def get_max_dist(mask):

            
            nodes = set()
            for i in range(n):
                if (1 << i) & mask:
                    nodes.add(i)

            if len(nodes) == 1:
                return

         

            e = []
            UF = UnionFind(N)

            m = 0
            base = 0
            for u, v in edges:
                if u in nodes and v in nodes:
                    e.append((u, v))
                    m |= (1 << u)
                    m |= (1 << v)
                    base = u
                    UF.union(u, v)

            if UF.rank[UF.find(base)] != len(nodes):
                return

            if m in self.seen:
                return

            self.seen.add(m)

            if not e:
                return

         


            A[find_tree_diameter(e)] += 1

            

        for i in range(1, 1<<n):
            get_max_dist(i)




        return A[1:]
   

        # print(A)
        # for x in A[1:]:
        #     total = 0
        #     for i in range(n):
        #         if (1 << i) & x:
        #             total += 1
        #     ans.append(total)
        # return ans


        




```
['Python3']