--- tags: Programming Contest --- # AtCoder Beginner Contest 174 題解 [題目連結](https://atcoder.jp/contests/abc174/tasks) ## 心得 解五題,排 1387 ## A, B 略 ## C. Repsept 序列的數字可能非常大,該怎麼高速地判定某大數是不是 K 的倍數呢?Horner's Rule!這個技巧真常出現。 同時序列的每一項是其前一項乘 10 再加 7,這就可以跟 Horner's Rule 結合在一起,所以我們可以高速的迭代序列的每項,同時判斷各項是不是 K 的倍數。問題是該最多該迭代幾項,都沒找到就判定不存在?比賽時我直接猜是 K 項,沒仔細思考,然後就過了。 賽後同學分析:因為是對 K 取餘數,餘數的可能為 [0, K - 1] ,共 K 個,所以取餘數超過 K 次結果必重覆,形成循環。當有循環時,還沒找到倍數即無解,所以檢查 K 項就能知道有沒有解。 ```python K = int(input()) def solve(): val = 0 for i in range(1, K + 1): val = val * 10 + 7 val = val % K if val == 0: return i return -1 print(solve()) ``` ## D. Alter Altar 畫出幾個 testcase 研究研究,發現二個結論: 1. 最終字串必形成 `RRR...WWWW` 的型式。 2. 沒必要「改顏色」,只使用「交換」所需的操作數就是最小的。 第二點是因為「交換」一次將兩個位置處理好,「改顏色」一次只能處理好一個位置。 於是我的方法很簡單:將最終字串建出來跟原字串比一下,看有幾個位置需要處理,該值除上 2 即為答案。 ```python N = int(input()) C = input() T = 'R' * C.count('R') + 'W' * C.count('W') ans = sum([1 for a, b in zip(C, T) if a != b]) // 2 print(ans) ``` ## E. Logs 最大值最小化,二分搜!對應的 check 為:看看是不是只需 K 刀或更少就能將 logs 分割成最長長度為 l,換一個講法是:將每個 log,都盡量切成最長的長度 l,看最後的總刀數是不是 <= K。這可以在 $O(N)$ 時間算出,因為在給定某個 log 的長度為 $a$ 時,將之分解成每塊最長長度都是 $l$ 所需的刀數為 $\lceil \frac{a}{l} \rceil - 1$,可以 $O(1)$ 得到。$\lceil \frac{a}{l} \rceil$ 代表的是最後有幾塊,減上 1 就是所需刀數。 ```python from math import ceil N, K = map(int, input().split()) A = list(map(int, input().split())) def check(l): cnt = sum(ceil(a / l) - 1 for a in A) return cnt <= K lb, ub = 0, max(A) for _ in range(50): mid = (lb + ub) / 2 if check(mid): ub = mid else: lb = mid print(ceil(ub)) ``` ## F. Range Set Query 這題好像是經典題,不過我沒解過。這題因為 $c_i$ 很大,我們無法將之塞進位元,然後用一個線段樹解決。 給定一個序列 `A`,我們建構一個序列 `B`,長度與 `A` 相同,且滿足一個特性:對於 `A` 中的每種數字 `d` 與 `d` 所在的那些位置,只有最右邊那個位置 `p`, `B[p] = 1`,`B` 的其他位置都是 `0`,以下給出一些例子: 序列 `A = [1, 2, 3]`,對應 `B = [1, 1, 1]`。 序列 `A = [1, 2, 1]`,對應 `B = [0, 1, 1]`。 序列 `A = [1, 1, 1]`,對應 `B = [0, 0, 1]`。 這樣的序列 B 讓我們有一個很好的特性,當我們想知道 A 有多少個不同值時,只要加總 B 就能得到結果,事實上這個結論對所有 A 的後綴都成立:**想知道 `A[i:]` 有多少不同值,答案為 `sum(B[i:])`**。 利用這個特性,對於題目所給的 C,我們可以設計出這樣的演算法: ```python for i in range(len(C)): 求出 C[:i] 所對應的 B 若存在 query 的右界是 i, 即 (l, i),那這個 query 的答案是 sum(B[l:]) ``` 顯然 `C[:i - 1]` 對應的 `B` 與 `C[:i]` 對應的 `B` 是非常像的。我們只需另一個陣列 `last` 記錄每種數字最後一次出現的地方,這樣當我們從 `i - 1` 迭代到 `i`,只需將 `B` 中位於 `last[C[i]]` 的那個 `1` 移至位置 `i`,就能高速地得到新的 `B`。另外為了高速的查詢 `B` 的後綴和,我們使用 SegTree 或 BIT 來存 `B`。 ```python seg = SegTree(len(N), 0) # B last = defaultdict(lambda: -1) # -1 = not appeared yet for i, c in enumerate(C): # Compute B corresponding to C[:i] and update last if last[c] != -1: seg.increment(last[c], -1) seg.increment(i, +1) last[c] = i # Compute ans if there are queries ending at i for query in queries_end_at_i: query.ans = seg.sum([query.l, query.r]) ``` 最後給出使用 BIT 的 C++ AC Code: ```cpp #include <iostream> #include <vector> #include <tuple> #include <algorithm> using namespace std; typedef tuple<int, int, int> t3i; // API use 0-based index while internal implementation use 1-based index template <class T> struct BIT { int NN; vector<T> data; BIT(int N, T default_value) { NN = N; data = vector<T>(NN + 1, default_value); } void add_at(int idx, T val) { for (int i = idx + 1; i <= NN; i += (i & (-i))) { data[i] += val; } } T sum_to(int idx) const { // [0, idx] T res = 0; for (int i = idx + 1; i >= 1; i -= (i & (-i))) { res += data[i]; } return res; } T sum_in(int l, int r) const { // [l, r] return sum_to(r) - sum_to(l - 1); } }; template <class T> ostream & operator << (ostream &out, const BIT<T> &bit) { out << "BIT(NN=" << bit.NN << ", "; out << "[ "; for (int i = 0; i < bit.NN; i++) { out << bit.sum_in(i, i) << " "; } out << "])"; return out; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; auto C = vector<int>(N, 0); for (int i = 0; i < N; i++) { cin >> C[i]; } auto queries = vector<vector<t3i>>(N, vector<t3i>()); for (int i = 0; i < Q; i++) { int l, r; cin >> l >> r; l--; r--; queries[r].push_back({i, l, r}); } auto ans = vector<int>(Q, -1); auto V = *max_element(C.begin(), C.end()); auto last = vector<int>(V + 1, -1); auto bit = BIT<int>(N, 0); for (int i = 0; i < N; i++) { if (last[C[i]] != -1) { bit.add_at(last[C[i]], -1); } bit.add_at(i, +1); last[C[i]] = i; for (auto&& [id, l, r]: queries[i]) { ans[id] = bit.sum_in(l, r); } } for (auto x : ans) { cout << x << endl; } return 0; } ``` :::success [< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/) :::