--- tags: Programming Contest --- # AtCoder Beginner Contest 223 題解 ## 心得 比賽中解五題,都一次 AC,開心(直到看到 vtuber 最後五分鐘竟然過 F),積分 +71。 另外雖然我聽不懂日語也看不懂日文,但仍想推廣一下這個 vtuber [RinSakamichi](https://atcoder.jp/users/RinSakamichi)。她三年前就出道,二年前開始打 atcoder,從以前只能解出三題到這次解出六題,一直在進步,但 youtube 上一直沒什麼觀眾,訂閱數也才 1.1K。若有人感興趣可以去追蹤一下。也許是出於興趣,她三年都沒什麼起色還一直做下去,讓人覺得了不起。 ## A, B (〃∀〃) ## C. [Doukasen](https://atcoder.jp/contests/abc223/tasks/abc223_c) 比賽時沒想到總花費時間除 2 就是相遇的時間,二分搜直接開幹。 二分搜相遇的位置,檢查函式為 check(pos) 看「從左邊到 pos 所花的時間」是否大於「從右邊到 pos 所花的時間」。 二分搜的下界是 0,上界是 A 的和。在這個區間中 check 的分佈是 0 0 0 1 1 1。題目所求就是最後一個 0 的位置。因為是在浮點數上二分搜,所以用固定次數的版本。 ```python N = int(input()) A, B = [], [] for _ in range(N): a, b = map(int, input().split()) A.append(a) B.append(b) S = sum(A) def check(pos): pref = 0 timeL = 0 for a, b in zip(A, B): if pref <= pos <= pref + a: d = pos - pref else: d = a timeL += d / b pref += d suff = 0 timeR = 0 for a, b in zip(reversed(A), reversed(B)): if S - (suff + a) <= pos <= (S - suff): d = S - suff - pos else: d = a timeR += d / b suff += d return timeL > timeR lb, ub = 0, S for _ in range(100): m = (lb + ub) / 2 if check(m): ub = m else: lb = m print(lb) ``` ## D. [Restricted Permutation](https://atcoder.jp/contests/abc223/tasks/abc223_d) 畫一下可以發現,將 input 轉成 DAG 後 topological sort 即可。字典序的部份就將 queue 轉成用 priority queue。 ```python from heapq import heappop, heappush N, M = map(int, input().split()) G = [[] for _ in range(N)] inD = [0 for _ in range(N)] for _ in range(M): u, v = map(int, input().split()) u, v = u - 1, v - 1 G[u].append(v) inD[v] += 1 que = [u for u in range(N) if inD[u] == 0] ans = [] while len(que) > 0: u = heappop(que) ans.append(u + 1) for v in G[u]: inD[v] -= 1 if inD[v] == 0: heappush(que, v) if len(ans) == N: print(' '.join(map(str, ans))) else: print(-1) ``` ## E. [Placing Rectangles](https://atcoder.jp/contests/abc223/tasks/abc223_e) ![](https://i.imgur.com/HbwFGZZ.png =x150) 在紙上畫了一下,去掉交換與旋轉,會發現就只有兩種分割方式。所以就兩種 case 都試試看可不可行。旋轉的部份透過 X, Y 的全排列,交換透過 A, B, C 的全排列解決。 ```python def ceil_div(x, y): return (x + y - 1) // y def check(x, y, a, b, c): # Case 1 ha = ceil_div(a, x) hb = ceil_div(b, x) hc = ceil_div(c, x) if ha + hb + hc <= y: return True # Case 2 ha = ceil_div(a, x) hbc = y - ha if hbc > 0: wb = ceil_div(b, hbc) wc = ceil_div(c, hbc) if wb + wc <= x: return True return False from itertools import permutations X, Y, A, B, C = map(int, input().split()) ans = False for (x, y) in permutations([X, Y]): for (a, b, c) in permutations([A, B, C]): if check(x, y, a, b, c): ans = True print('Yes' if ans else 'No') ``` ## F. [Parenthesis Checking](https://atcoder.jp/contests/abc223/tasks/abc223_f) 官方題解看不懂,看了前面說的 vtuber 的 [submission](https://atcoder.jp/contests/abc223/submissions/26651828) 才想通。 沒寫過類似題這題不太可能自己想出來吧,我覺得比第五題難上不少。 將左括號視為 +1,右括號視為 -1,得到的數列的**前綴**為 P: | S: | ( | ( | ( | ) | ) | ) | ( | | --- | --- | --- | --- | --- | --- | --- | --- | | P: | 1 | 2 | 3 | 2 | 1 | 0 | 1 | 如果詢問的區間 [l, r] 是 correct 的,也就是說 1. S[l] = '(' 且 S[r] = ')' 2. [l, r] 的左括號數 = 右括號數,即 P[r] - P[l - 1] = 0。這個條件只考慮了數量,沒考慮順序是不是合理的,順序的部份為條件 3。 3. 這個區間中的 P 值都不能小於 P[l] - 1。若發生,代表從左至右遇到的右括號比左括號多。另外 P[r] 必需是 P[l] - 1,所以 min(P[l], P[l + 1], ..., P[r]) = P[l] - 1。 使用線段樹來存 P,因為我們想知道任意區間的最小值來實作條件 3。 交換操作的情況如下: | S: | ( | ( | ) | ) | ) | ( | ( | | --- | --- | --- | --- | --- | --- | --- | --- | | P: | 1 | 2 | 1 | 0 | -1 | 0 | 1 | | | | | l | | | r | | 跟前面的表格相比,可以發現: 1. l 以左(不含 l)的 P 值不變。 2. r 以右(包含 r)的 P 值不變。 3. [l, r - 1] 的 P 值都 -2,這是因為位置 l 從左括號變成右括號,因為 P 是前綴,所 [l, r - 1] 的值都 -2 了。 ```cpp #include <algorithm> #include <atcoder/lazysegtree> #include <iostream> #include <vector> using namespace std; using S = int; using F = int; S op(S a, S b) { return min(a, b); } S default_val() { return 0x3f3f3f3f; } S apply_lazy(F lazy, S val) { return lazy + val; } F push_lazy(F parent, F child) { return parent + child; } F default_lazy() { return 0; } using SegTree = atcoder::lazy_segtree<S, op, default_val, F, apply_lazy, push_lazy, default_lazy>; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, Q; cin >> N >> Q; string S; cin >> S; vector<int> P(N, 0); P[0] = ((S[0] == '(') ? +1 : -1); for (int i = 1; i < N; i++) { P[i] = P[i - 1] + ((S[i] == '(') ? +1 : -1); } SegTree seg(P); while (Q--) { int cmd, l, r; cin >> cmd >> l >> r; l--; r--; if (cmd == 1) { if (S[l] == '(' and S[r] == ')') { seg.apply(l, r, -2); swap(S[l], S[r]); } else if (S[l] == ')' and S[r] == '(') { seg.apply(l, r, +2); swap(S[l], S[r]); } } else { // closed interval [l, r] // endpoint check bool check1 = S[l] == '(' and S[r] == ')'; // bracket balance bool check2 = seg.get(r) - ((l > 0) ? seg.get(l - 1) : 0) == 0; // cannot drop under P[l] - 1 bool check3 = seg.prod(l, r + 1) == seg.get(l) - 1; if (check1 and check2 and check3) { cout << "Yes" << endl; } else { cout << "No" << endl; } } } return 0; } ``` :::success [< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/) :::