---
# System prepended metadata

title: CSES Problem Set — Finding a Centroid 題解
tags: [CSES題解]

---

---
tags: CSES題解
---

# CSES Problem Set — Concert Tickets 題解

## 題目
給定一顆有 $n$ 個節點的樹，你的目標是找到是*樹重心*，也就是若該節點為根，則所有子節點形成的子樹最多有 $\lfloor \frac{n}{2} \rfloor$ 個節點。

### 輸入
- 第一行有一個正整數 $n$，所有節點編號為 $1, 2, \ldots ,n$。($1 \leq n \leq 2 \times 10^5$)
- 接下來有 $n-1$ 行，每一行有兩個正整數 $u_i, v_i$，代表第 $u_i$ 跟第 $v_i$ 個節點之間有邊。($1 \leq u_i, v_i \leq n$)

### 輸出

輸出一個正整數代表是樹重心的節點，所有多個答案則輸出任意一個。

## 範例測資
```
Input:
5
1 2
2 3
3 4
3 5

Output:
3
```

![image](https://hackmd.io/_uploads/BymGZHVEA.png)

## 想法
先備知識：[Subordinates](https://cses.fi/problemset/task/1674)。

已知我們可以透過 DFS 知道以 $1$ 為根時，再以每一個節點為根的子樹大小。（自己設做 $s_i$）

![image](https://hackmd.io/_uploads/rJx1VSE4A.png)

對於每個節點，$n-s_i$ 即為非子樹的大小，此時我們只需檢查他的每一個孩子的大小跟 $n-s_i$ 不會超過  $\lfloor \frac{n}{2} \rfloor$ 就代表是樹重心。

:::info
範例：
對節點 $3$ 來說，他要檢查 $s_4$、$s_5$ 跟 $n-s_3$（也就是節點 $1, 2$ 形成的子樹大小）是否超過 $\lfloor \frac{5}{2} \rfloor$，結果為真，引此它是樹重心。
:::

## 程式
```cpp=
#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 2e5+10;

int n;
int u, v;
vector<vector<int>> G(MAX_N);
vector<int> subtree_size(MAX_N);

int dfs(int now, int pre){
    int ret = -1; // 如果還沒找到重心就回傳 -1
    bool check = true; // now 是否為重心
    subtree_size[now] = 1;

    for (auto x : G[now]){
        if (x==pre) continue;
        
        int res = dfs(x, now);
        if (res!=-1) ret = res;
        subtree_size[now] += subtree_size[x];
        if (subtree_size[x]>n/2) check = false;
    }
    if (n-subtree_size[now]>n/2) check = false;
    if (check) ret = now;

    return ret;
}

int main(){

    // input
    cin >> n;
    for (int i=0 ; i<n-1 ; i++){
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    // process
    int centroid = dfs(1, 0);

    // output
    cout << centroid << "\n";

    return 0;
}
```