# APCS實作題2024年10月第4題:搭到終點 > 日期:2024年10月31日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=o714) <br /> ## 題目 ### 問題描述 有一個數線從 $0$ 到 $m$,你一開始在位置 $0$ 上,想要搭公車到位置 $m$ 處。 有 $n$ 台公車路線可以選擇,每一公車路線都有其行駛的起點和終點。 例如你想要到位置 $m = 9$,且有 $n = 5$ 條公車路線如下 | 公車路線編號 | 1 | 2 | 3 | 4 | 5 | | --------- | -- | -- | -- | -- | -- | | 路線起終點 | [0, 4] | [4, 6] | [0, 6] | [3, 7] | [5, 9] | 你可以任意地決定公車何時出發,並且在公車的路線範圍內都可以上車,但一定會搭到該路線的終點才下車,且不可在同一位置同時上下車。 你想知道總共有幾種搭車方式可以到位置 $m$。如上述例子總共有 $7$ 種搭車方式,分別如下列: 1. 1 -> 2 -> 4 -> 5 (先搭乘路線 $1$ 到位置 $4$,再搭乘路線 2 到位置 $6$,接著搭乘路線4 到位置 $7$,最後搭乘路線 5 到位置 $9$) 2. 1 -> 2 -> 5 3. 1 -> 3 -> 4 -> 5 4. 1 -> 3 -> 5 5. 1 -> 4 -> 5 6. 3 -> 4 -> 5 7. 3 -> 5 由於搭乘方式數量可能很大,請輸出搭乘方式數量 mod $p$ 的結果。 <br /> ### 輸入格式 第一行有三個正整數 $n, m, p ~(1 \leq n \leq 2 \times 10^5,~ 1 \leq m \leq 10^9,~ 1 \leq p \leq 10^9 + 9)$ 代表有 $n$ 個公車路線,終點在 $m$ 以及答案要 mod 的整數 $p$。 接下來一行有 $n$ 個數字,代表每一個公車路線的起始位置。 最後一行有 $n$ 個數字,代表每一個公車路線的終點位置。 子題配分 - 20%:$1 \leq n, m \leq 100$ - 40%:$1 \leq m \leq 10^5$ - 40%:無限制 <br /> ### 輸出格式 輸出共有幾種公車搭乘方式,由於答案數字可能很大,請輸出答案 mod $p$ 的結果。 <br /> ### 範例輸入1 ``` 5 9 11 0 4 0 3 5 4 6 6 7 9 ``` ### 範例輸出1 ``` 7 ``` ### 範例輸入2 ``` 6 8 4 0 1 2 3 5 6 3 6 6 6 8 8 ``` ### 範例輸出2 ``` 2 ``` <br /> ## Python 程式碼 吳邦一教授的寫法,來源[在此](https://hackmd.io/@bangyewu/rJ5yJqEe1x#%E7%AC%AC%E5%9B%9B%E9%A1%8C-%E6%90%AD%E5%88%B0%E7%B5%82%E9%BB%9E-ZeroJudge-o714),我想不到更好的寫法。 費時最久約 0.9 s,使用記憶體最多 52 MB,通過測試。 ```python= from bisect import bisect_left n, m, p = map(int, input().split()) # 公車路線數 n,目的地在 m,答案取 p 的餘數 start = list(map(int, input().split())) # 公車起點 end = list(map(int, input().split())) # 公車終點 seg = sorted(zip(end, start)) # 合併公車終點、起點,以終點排序 dp = [1] # 到達各站的搭法數量前綴和,到達起點 0 搭法有 1 種 point = [0] # 最後所在的車站編號 for en, st in seg: # 依序讀取各路線的起點、終點 if point[-1] != en: # 如果這輛公車的的終點不等於 point 最後一項 point.append(en) # 更新 point dp.append(dp[-1]) # 更新 dp if st == 0: # 如果這輛公車的起點是 0 dp[-1] = (dp[-1] + dp[-2]) % p # dp[-1] 的數量要再加上到此站之前所有的搭法數量 else: # 其它狀況 i = bisect_left(point, st) - 1 # 用二分搜尋法從 point 之中找 st 插入的索引值再減 1 dp[-1] = (dp[-1] + dp[-2] - dp[i] + p) % p # 前面全部的總和減去前i站的總合,取餘數 # end of for loop if point[-1] != m: ans = 0 # 搭不到目的地,答案為 0 else: ans = (dp[-1] - dp[-2] + p) % p # 更新答案 print(ans) ``` <br /><br /> ## C++ 程式碼 費時最久約 0.4 s,使用記憶體最多 6.1 MB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <utility> #include <algorithm> typedef long long LL; using namespace std; int main() { int n, m, p; cin >> n >> m >> p; // 公車路線數 n,目的地在 m,答案取 p 的餘數 vector<pair<int, int>> seg (n); // 公車終點、起點 for(int i=0; i<n; i++) cin >> seg[i].second; for(int i=0; i<n; i++) cin >> seg[i].first; sort(seg.begin(), seg.end()); // 以終點排序 vector<LL> dp = {1}; // 到達各站的搭法數量前綴和,到達起點 0 搭法有 1 種 vector<int> point = {0}; // 最後所在的車站編號 for(int i=0; i<n; i++) { // 依序讀取各路線的起點、終點 int en = seg[i].first, st = seg[i].second; if (point.back() != en) { // 如果這輛公車的的終點不等於 point 最後一項 point.push_back(en); // 更新 point dp.push_back(dp.back()); // 更新 dp } if (st == 0) { // 如果這輛公車的起點是 0 dp[dp.size()-1] = (dp[dp.size()-1] + dp[dp.size()-2]) % p; // dp[dp.size()-1] 的數量要再加上到此站之前所有的搭法數量 } else { // 其它狀況 int i = lower_bound(point.begin(), point.end(), st) - point.begin() - 1; // 用二分搜尋法從 point 之中找 st 插入的索引值再減 1 dp[dp.size()-1] = (dp[dp.size()-1] + dp[dp.size()-2] - dp[i] + p) % p; // 前面全部的總和減去前i站的總合,取餘數 } } int ans; if (point.back() != m) ans = 0; // 搭不到目的地,答案為 0 else ans = (dp[dp.size()-1] - dp[dp.size()-2] + p) % p; // 更新答案 cout << ans << "\n"; return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`