# 2024六月APCS題解 ## [1. 特技表演](https://zerojudge.tw/ShowProblem?problemid=o076) 大意:求出給定長度為$N$的序列$\left\langle A_i \right\rangle$的最長嚴格下降子區間的長度。 解法:可以把這個序列切成很多嚴格下降子區間——當$A_{i+1} \ge A_i$時,$A_{i+1}$會是一個新的嚴格下降子區間的開頭,然後要求的是以$A_i$為結尾的嚴格下降子區間的長度最長是多少。 時間複雜度:$O(N)$。 空間複雜度:$O(1)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> #define ALL(x) begin(x), end(x) using namespace std; using i64 = long long; template <class T> using vec = vector<T>; int main() { cin.tie(nullptr)->sync_with_stdio(false); int n; cin >> n; int pre = numeric_limits<int>::max() / 2; int ans = 0; for (int x, cnt = 0; n--; ) { cin >> x; if (x >= pre) cnt = 0; ans = max(ans, ++cnt); pre = x; } cout << ans << '\n'; } ``` ::: ## [2. 電子畫布](https://zerojudge.tw/ShowProblem?problemid=o077) 大意:有$N$次操作,要把和$(r,c)$的曼哈頓距離$\le t$的格子的值都加上$x$,問最後每個格子的值是多少。 解法:每次操作都去看所有格子有沒有滿足上面講的條件,如果有的話才把值加上$x$。 時間複雜度:$O(HWN)$。 空間複雜度:$O(HW)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> #define ALL(x) begin(x), end(x) using namespace std; using i64 = long long; template <class T> using vec = vector<T>; int main() { cin.tie(nullptr)->sync_with_stdio(false); int h, w, q; cin >> h >> w >> q; vec<int> val(h * w, 0); for (int r, c, t, x; q--; ) { cin >> r >> c >> t >> x; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { if (abs(r - i) + abs(c - j) <= t) val[i * w + j] += x; } } } for (int i = 0; i < h * w; i++) cout << val[i] << " \n"[(i + 1) % w == 0]; } ``` ::: ## [3. 缺字問題](https://zerojudge.tw/ShowProblem?problemid=o078) 大意:給定一個大小為$K$個字母的集合和字串$S$,求出在使用該集合所組出長度為$L$的字串中,不為字串$S$的子字串的最小字典序字串為何。 解法:先預處理每個字母在字母集中比多少人大,記為$\text{id}(c)$,之後定義$f(l,r)=\sum_{i=l}^{r-1}\text{id}(s_i) \times K^{r-1-i}$,於是就可以使用$f(l,r)$的值來比較字典序,$f$越小字典序越小。最後要求的就是$\text{mex}(f(0, L), f(1, L+1), \dots, f(|S|-L, |S|))$表示的字串。 另一個解法是:先把$S_{0 \sim L},S_{1 \sim L+1}, \dots, S_{(|S|-L) \sim |S|}$這些字串丟進一個set裡面,再用dfs的方式枚舉所有$K^L$個字串(其實因為$\text{mex}$的性質,最多跑$|S|-L+2$個字串就一定會找到),依照字典序枚舉,第一個不在那個set裡面的字串就是答案了。 第一種解法的時間複雜度:$O(|S|)$、空間複雜度:$O(|S|)$。 第二種解法的時間複雜度:$O(L|S| \log |S|)$、空間複雜度:$O(L|S|)$。 :::spoiler code(第一種解法): ```cpp= #include <bits/stdc++.h> #define ALL(x) begin(x), end(x) using namespace std; using i64 = long long; template <class T> using vec = vector<T>; constexpr int maxN = 300000; bitset<maxN> vis; int pre[26]; char mp[10]; int main() { cin.tie(nullptr)->sync_with_stdio(false); int S_len = 0; auto id = [](char c) -> int { return pre[c - 'a'] - 1; }; { string s; cin >> s; S_len = s.length(); for (auto c : s) ++pre[c - 'a']; for (int i = 1; i < 26; i++) pre[i] += pre[i - 1]; for (auto c : s) mp[id(c)] = c; } int len; cin >> len; string s; cin >> s; int N = (int)s.length(); int hash_val = 0, mul = 1; for (int i = 0; i < len; i++) { hash_val = hash_val * S_len + id(s[i]); if (i) mul *= S_len; } if (hash_val < N) vis[hash_val] = true; for (int i = len; i < N; i++) { hash_val -= id(s[i - len]) * mul; hash_val = hash_val * S_len + id(s[i]); if (hash_val < N) vis[hash_val] = true; } string ans; int x = [&]() -> int { for (int i = 0; i < N; i++) if (!vis[i]) return i; return N; }(); for (int t = len; t--; ) { ans.push_back(mp[(x % S_len)]); x /= S_len; } reverse(ALL(ans)); cout << ans << '\n'; } ``` ::: ## [4. 最佳選擇](https://zerojudge.tw/ShowProblem?problemid=o079) 大意:給一個長度為$N$的正整數序列$\left\langle A_i \right\rangle$,你可以做0次以上的操作——拿走這個序列的第一個或最後一個數字,做完操作之後,拿走的所有數字裡面,偶數和奇數要一樣多,且他們的和不超過$K$,問最大的和可以是多少? 解法:可以拿的其實是一個前綴和一個不和那個前綴重疊的後綴,所以對每個前綴和$\text{pre}_i$,前綴奇數偶數數量差$\text{diff}_i$,用$\text{diff}_i$分類來存$(\text{pre}_i,i)$的pair,之後枚舉後綴查詢和他配的最好的前綴是多少,注意前綴會因為他的位置而過期。 時間複雜度:$O(N)$。 空間複雜度:$O(N)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> #define ALL(x) begin(x), end(x) using namespace std; using i64 = long long; template <class T> using vec = vector<T>; constexpr int maxDIFF = 2000; vec<pair<int, int>> pref[maxDIFF * 2 + 1]; int main() { cin.tie(nullptr)->sync_with_stdio(false); auto add = [](int diff, int pre, int i) -> void { pref[diff + maxDIFF].emplace_back(pre, i); }; auto is_empty = [](int diff) -> bool { return pref[diff + maxDIFF].empty(); }; auto top = [](int diff) { return pref[diff + maxDIFF].back(); }; auto pop = [](int diff) -> void { pref[diff + maxDIFF].pop_back(); }; int n, k; cin >> n >> k; vec<int> v(n); for (int i = 0, diff = 0, pre = 0; i < n; i++) { cin >> v[i]; diff += ((v[i] & 1) ? 1 : -1); pre += v[i]; add(diff, pre, i); } int ans = ((is_empty(0) || top(0).first > k) ? 0 : top(0).first); for (int i = n - 1, diff = 0, suf = 0; i >= 0; i--) { diff += ((v[i] & 1) ? 1 : -1); suf += v[i]; if (suf > k) break; if (diff < -maxDIFF || diff > maxDIFF) continue; while (!is_empty(-diff) && (top(-diff).second >= i || top(-diff).first + suf > k)) pop(-diff); if (diff == 0) ans = max(ans, suf); if (!is_empty(-diff)) ans = max(ans, suf + top(-diff).first); } cout << ans << '\n'; } ``` :::