# 2025/06/15 APCS ## 前言 最後一次舊制APCS,因為我APCS還在 4 4 所以想說再來考一下 (上次沒蹭到觀念5級 真的too bad) ## 題目 ### pA. [跳柵遊戲](https://zerojudge.tw/ShowProblem?problemid=q836) 題目大概是在講 一開始每次可以走 $N$ 步 假如你走到一個整數點為 $A$ 的倍數的點,那麼你的可以走的步數會 $-B$ 假如你走到一個整數點為 $C$ 的倍數的點,那麼你的可以走的步數會 $-D$ 問你最後會停在哪個點 解法大概是 `while` 炸過去,我猜複雜度是好的 ```cpp= #include <iostream> using namespace std; int main() { int n; cin >> n; int a, b, c, d; cin >> a >> b >> c >> d; int now = 0; while(n > 0) { now += n; if(now%a == 0) n -= b; if(now%c == 0) n -= d; } cout << now << '\n'; } ``` ### pB. [轉盤得分](https://zerojudge.tw/ShowProblem?problemid=q837) 題目講 你有 $M$ 個有 $N$ 個刻度的轉盤 每個轉盤上的每個刻度有各自的字母 今天要你模擬轉轉盤,且過程中會獲得分數 分數就是當你轉完 $M$ 個轉盤時,你會依照每個 $1 \sim N$ 的刻度上字母出現最大頻率來給分 反正就是實作題,但取模要取好 阿不可能真的對字串轉轉轉,所以用一個陣列 `it` 紀錄每個轉盤目前的開始刻度 ```cpp= #include <iostream> #include <vector> using namespace std; int main() { int m, n, k; cin >> m >> n >> k; vector<string> v(m); for(int i = 0; i < m; i++) cin >> v[i]; vector<int> it(m); int ans = 0; while(k--) { for(int i = 0; i < m; i++) { int a; cin >> a; it[i] = ((it[i] - a)%n+n)%n; } for(int i = 0; i < n; i++) { vector<int> arr(26); int mx = 0; for(int j = 0; j < m; j++) mx = max(mx, ++arr[v[j][(it[j] + i)%n]-'a']); ans += mx; } } cout << ans << '\n'; } ``` ### pC. [貪心闖關](https://zerojudge.tw/ShowProblem?problemid=q838) 題目大概就是 有很多沙袋排成一排 每一格都有很多個沙袋 每次你要從最少沙袋的那格把沙袋往後面有沙袋丟 假如在最後就直接丟掉 你在過程中會獲得你搬的沙袋數量的分數 假如你搬不動沙袋 ($\text{沙袋數量} > T$),或者沒得搬了 程式就停止了 我第一直覺是類似柯朵莉樹(ODT)的 set 亂做 大概 $O(N \lg N)$ 複雜度,加一些常數,感覺很穩 ```cpp= #include <iostream> #include <set> using namespace std; int main() { int n, t; cin >> n >> t; set<pair<long long, long long> > se1, se2; for(int i = 0; i < n; i++) { int a; cin >> a; se1.insert({i, a}); se2.insert({a, i}); } long long ans = 0; while(se2.size()) { auto it = se2.begin(); auto [a, b] = *it; if(a > t) break; auto lb = se1.lower_bound(make_pair(b, a)); ans += a; if(next(lb) == se1.end()) { se1.erase(lb); se2.erase({a, b}); continue; } auto [c, d] = *next(lb); se1.erase(lb); se1.erase({c, d}); se1.insert({c, d+a}); se2.erase({a, b}); se2.erase({d, c}); se2.insert({d+a, c}); } cout << ans << '\n'; } ``` ### pD. [最遠分組](https://zerojudge.tw/ShowProblem?problemid=q839) 題目就是說有 $N$ 個數字 你要把他們分成 $K$ 組 每個不同的數字間會有距離 你要很會分組,分出來,讓不同組之間的數字的最小距離最大化 看到題目感覺好難,不知道要怎麼下手 因為怎麼看都不像 DP or Graph 感覺連演算法都牽扯不上 然後想到的 DSU 又不在考綱中 但真的沒想到其他做法,所以唬爛一個 DSU 出來 ```cpp= #include <iostream> #include <vector> #include <algorithm> #include <array> using namespace std; struct DSU { vector<int> p; int com; DSU(int x) : p(x, -1), com(x) {} int fp(int id) {return p[id] < 0 ? id : p[id] = fp(p[id]);} void upd(int a, int b) { int ar = fp(a), br = fp(b); if(ar != br) { if(p[ar] > p[br]) swap(ar, br); p[ar] += p[br]; p[br] = ar; com--; } } bool same(int a, int b) {return fp(a) == fp(b);} }; int main() { int n, k; cin >> n >> k; vector<vector<int> > dis(n, vector<int> (n)); vector<array<int, 3> > v; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { cin >> dis[i][j]; if(j > i) v.push_back({dis[i][j], i, j}); } } sort(v.begin(), v.end()); DSU dsu(n); for(auto &[a, b, c] : v) { if(dsu.com == k) break; if(!dsu.same(b, c)) dsu.upd(b, c); } int ans = 1e9; for(int i = 0; i < n; i++) { for(int j = i+1; j < n; j++) if(!dsu.same(i, j)) ans = min(ans, dis[i][j]); } cout << ans << '\n'; } ``` ## 心得 這次 APCS 感覺放很多水 實作題簡單到讓我懷疑我的想法出錯了 花了一個多小時在寫對拍 ~~然後最後沒事做提早出場~~ 觀念題是我第一次靜下心來仔細寫,感覺蠻有收穫的 然後 APCS 果然有一大堆國中國小生來考 我考試座位旁邊有一個暴躁老哥 摔滑鼠 + 扭來扭去 + 打嗝 很躁