2421.Number of Good Paths
===
###### tags: `Hard`,`Array`,`Tree`,`Graph`,`Union Find`
[2421. Number of Good Paths](https://leetcode.com/problems/number-of-good-paths/)
### 題目描述
There is a tree (i.e. a connected, undirected graph with no cycles) consisting of `n` nodes numbered from `0` to `n - 1` and exactly `n - 1` edges.
You are given a **0-indexed** integer array `vals` of length `n` where `vals[i]` denotes the value of the i^th^ node. You are also given a 2D integer array `edges` where `edges[i]` = [$a_i$, $b_i$] denotes that there exists an **undirected** edge connecting nodes $a_i$ and $b_i$.
A **good path** is a simple path that satisfies the following conditions:
The starting node and the ending node have the **same** value.
All nodes between the starting node and the ending node have values **less than or equal to** the starting node (i.e. the starting node's value should be the maximum value along the path).
Return *the number of distinct good paths.*
Note that a path and its reverse are counted as the **same** path. For example, `0 -> 1` is considered to be the same as `1 -> 0`. A single node is also considered as a valid path.
### 範例
**Example 1:**
![](https://assets.leetcode.com/uploads/2022/08/04/f9caaac15b383af9115c5586779dec5.png =50%x)
```
Input: vals = [1,3,2,1,3], edges = [[0,1],[0,2],[2,3],[2,4]]
Output: 6
Explanation: There are 5 good paths consisting of a single node.
There is 1 additional good path: 1 -> 0 -> 2 -> 4.
(The reverse path 4 -> 2 -> 0 -> 1 is treated as the same as 1 -> 0 -> 2 -> 4.)
Note that 0 -> 2 -> 3 is not a good path because vals[2] > vals[0].
```
**Example 2:**
![](https://assets.leetcode.com/uploads/2022/08/04/149d3065ec165a71a1b9aec890776ff.png =50%x)
```
Input: vals = [1,1,2,2,3], edges = [[0,1],[1,2],[2,3],[2,4]]
Output: 7
Explanation: There are 5 good paths consisting of a single node.
There are 2 additional good paths: 0 -> 1 and 2 -> 3.
```
**Example 3:**
![](https://assets.leetcode.com/uploads/2022/08/04/31705e22af3d9c0a557459bc7d1b62d.png =30%x)
```
Input: vals = [1], edges = []
Output: 1
Explanation: The tree consists of only one node, so there is one good path.
```
**Constraints**:
* `n` == `vals.length`
* 1 <= `n` <= 3 * 10^4^
* 0 <= `vals[i]` <= 10^5^
* `edges.length` == `n` - 1
* `edges[i].length` == 2
* 0 <= $a_i$, $b_i$ < `n`
* $a_i$ != $b_i$
* `edges` represents a valid tree.
### 解答
#### C++
##### 思路:
1. 找path, 頭尾值需相同, path途中值小於等於頭尾
2. 如何找到及遍歷符合的path? 如何避免重複path?
3. 看答案
4. 從最小值的node開始做, 把出現過的node可以連接的當作一群, 每有新的node加入群時, 因為是從值小的部分開始做, 加入的node形成的path勢必合法, 故結果只需看新加入群的node有幾個即可算可組成多少path
##### 做法:
1. adjacent list 紀錄每個node與其相連值較小或等於的node
2. 建立map<int, vector<int>>代表某個值包含哪些node
3. 由小至大處理每一個值的nodes
4. 用Union find連接當前處理的nodes跟之前的nodes, 形成可能不只一個的component(可用編號小的當component head)
5. 遍歷nodes確認各別屬於哪每一個component, cnt表示每一個component出現有cnt個node是新加入的, 這些可以組成cnt*(cnt-1)/2個path + 每一個node單獨形成一個path共有cnt個
6. 重複3~6
```cpp=
class Solution {
public:
class UnionFind{
private:
vector<int> id;
public:
UnionFind(int cnt){
id = vector<int>(cnt);
for(int i = 0; i < cnt; i++)
id[i] = i;
}
int find(int p){
if(p == id[p]) return p;
int root = find(id[p]);
id[p] = root;
return root;
}
void connect(int p, int q){
int i = find(p), j = find(q);
if(i == j) return;
if(i > j)
id[i] = j;
else
id[j] = i;
}
};
int numberOfGoodPaths(vector<int>& vals, vector<vector<int>>& edges) {
int n = vals.size(), goodPaths = 0;
vector<vector<int>> adj(n);
map<int, vector<int>> sameValues;
for(auto& e : edges){
int u = e[0], v = e[1];
if(vals[u] >= vals[v])
adj[u].push_back(v);
else if(vals[v] >= vals[u])
adj[v].push_back(u);
}
for(int i = 0; i < n; i++)
sameValues[vals[i]].push_back(i);
UnionFind uf(n);
for(auto &[value, allNodes] : sameValues){
for(int u : allNodes){
for(int v : adj[u]){
uf.connect(u, v);
}
}
unordered_map<int, int> group;
for(int u : allNodes)
group[uf.find(u)]++;
for(auto &[_, size] : group)
goodPaths += (size * (size-1) / 2);
goodPaths += allNodes.size();
}
return goodPaths;
}
};
```
> [name=XD] [time= Jan 15, 2023]
Time: $O(n+m*log(m))$ `n`為node數, `m`為node中不同的值
Extra Space: $O(n)$
### Reference
[回到題目列表](https://hackmd.io/@Marsgoat/leetcode_every_day)