---
# System prepended metadata

title: 'Orange Lecture #04 - Distance in tree'

---

# Orange Lecture #04 - Distance in tree

### 1, Đề bài
- Cho một cây vô hướng n đỉnh.
Đếm số cặp đỉnh có khoảng cách bằng k

#### Input:
- n k
- n - 1 dòng tiếp theo:
    - a b mô tả một cạnh
#### Output:
- kết quả

![](https://i.imgur.com/zWFFznI.png)
![](https://i.imgur.com/qJLTVNU.png)

### 2, Ý tưởng & thuật toán

a) Dfs từ mọi đỉnh

```python
result = 0

# dfs function
def dfs(u, distanceFromRoot = 0):
    visited[u] = True
    if(distanceFromRoot == k): result += 1
        
    # from u, traverse to u's child nodes
    for v in graph[u]:
        if(visited[v]): continue
        dfs(v, distanceFromRoot + 1)
        
For u = 1 -> n:
    visited = [False] * n
    dfs(u) # u is the root
    
print(result//2)
```
=> O(N^2)

b) Dfs

- Ý tưởng: **dist(u, v) = dist(u, c) + dist(v, c) với c = lca(u,v)** là tổ tiên chung gần nhất của u và v

![](https://i.imgur.com/Q2zUyg4.jpg)


- **Với mỗi node c: có bao nhiêu cặp (u, v) có lca là c và khoảng cách = k?**
=> u, v phải thuộc 2 nhánh con khác nhau của c
+ Giả sử 2 nhánh con đó là v1 và v2, u thuộc v1 và v thuộc v2

    => dist(u, v) = dist(u, c) + dist(v, c) = dist(u, v1) + 1 + dist(v, v2) + 1 = k
    <=> **dist(u, v1) = k - 2 - dist(v, v2)**
    Đặt **leftDist** = dist(u, v1), **rightDist** = dist(v, v2)
    
![](https://i.imgur.com/Kfe66ho.jpg)

```python
for c:
    for v1 != v2: #v1, v2 are children of c
        for leftDist = 0 -> k - 2:
            rightDist = k - 2 - leftDist
            result += countDescendants(v1, leftDist) * countDescendants(v2, rightDist)
            # countDescendants(x, d) = number of descendants of x whose distance to x = d
```

- Làm sao để tính countDescendants(x, d)?
    + **countDescendants[x][d] = sum(countDescendants[y][d - 1])** với y là node con của x
    
    ![](https://i.imgur.com/KFVKGHO.jpg)


```python
result = 0

bool visited[N]
int distanceFromRoot[N]
int countDescendants[N][K]
list graph[N] # graph[u] = list of neighbors of u

# dfs function
def dfs(c, parent = 0):
    distanceFromRoot[c] = distanceFromRoot[parent] + 1
    visited[c] = True
    
    countDescendants[c][0] = 1 # only c
    
    if(distanceFromRoot[c] >= K): result += 1

    # from u, traverse to u's child nodes
    for v in graph[c]:
        if(visited[v]): continue
        dfs(v, c)
        
        for i = 1 -> K:
            countDescendants[c][i] += countDescendants[v][i - 1]

    # If c is the lca, how many pairs (u, v) with lca = c and dist(u, v) = k there are?
    
    for v1 in graph[c]:
        if(v1 == parent): continue
        for v2 in graph[c]:
            if(v2 == parent || v1 > v2): continue
            ### Consider each pair of two different subbranches of c
            for leftDist = 0 -> k - 2:
                rightDist = k - 2 - leftDist
                #
                result += countDescendants(v1, leftDist) * countDescendants(v2, rightDist)            
        
dfs(1) # 1 is the root

print(result)
```

=> O(N^2 * K) (worst case là tất cả các node đều là node con của 1)

c) Tối ưu
- Nhận xét: Với mỗi v2 (bên phải), phải xét tất cả các v1 bên trái nó để nhân với countDescendants[v1][]

=> Gom tất cả các v1 bên trái này lại và tính toán với v2 cùng một lúc
=> Mỗi lần đi qua một đỉnh con v của c thì cộng vào countDescendants[c][]

```python 
def dfs(c, parent)
    ...
    for v2 in graph[c] & v != parent:
        
        # consider v as "v2" - the right branch
        for rightDist = 0 -> k - 2:
            leftDist = k - 2 - rightDist
            
            # now all the previous subbranch has been accumulated into countDescendants[c]
            result += countDescendants[c][leftDist] * countDescendants[v2][rightDist]
        
        countDescendants[c][] += countDescendants[v2][]
    ...
```

```python
result = 0

bool visited[N]
int distanceFromRoot[N]
int countDescendants[N][K]
list graph[N] # graph[u] = list of neighbors of u

# dfs function
def dfs(c, parent = 0):
    distanceFromRoot[c] = distanceFromRoot[parent] + 1
    visited[c] = True
    
    if(distanceFromRoot[c] >= K): result += 1

    
    # If c is the lca, how many pairs (u, v) with lca = c and dist(u, v) = k there are?
    # 
    # from c, traverse to u's child nodes
    for v2 in graph[c]:
        if(visited[v2]): continue
        dfs(v2, c)
        
        for rightDist = 0 -> k - 2:
            leftDist = k - 2 - rightDist
            
            # now all the previous subbranch has been accumulated into countDescendants[u]
            result += countDescendants[c][leftDist] * countDescendants[v2][rightDist]
        
        for i = 0 -> K:
            countDescendants[c][i] += countDescendants[v2][i] # not i - 1
            
    # update countDescendants[c][i]
    for i = K - 1 -> 0:
        countDescendants[c][i + 1] = countDescendants[c][i]
    
    countDescendants[c][0] = 1 # only c

dfs(1) # 1 is the root

print(result)
```

4, Pseudocode

    
5, Complexity
- O(N * K) (Mỗi v2 chỉ có một parent nên chỉ được tính một lần)