【AtCoder】PythonでABC328を解く
はじめに
abc328のA,B,C,D,E問題をPython3で解きます。
A問題
問題の概要
配点が以下である問題すべての配点の合計を出力せよ。
問題の考察
リスト内包表記とsum関数を使うと楽に実装できます。
コード
def main(): N, X = map(int, input().split()) S = list(map(int, input().split())) ans = sum(s for s in S if s <= X) print(ans) if __name__ == "__main__": main()
B問題
問題の概要
月日 ()がゾロ目になる日付の日数を数えよ。
問題の考察
日付を全探索し、各日付を文字列に直して集合のサイズが1か調べることでゾロ目を判定します。
コード
def solve(N, D): repdigit_dates = [] for i, d in enumerate(D, 1): for j in range(1, d + 1): date = f"{i}{j}" if len(set(date)) == 1: repdigit_dates.append(date) return repdigit_dates def main(): N = int(input()) D = list(map(int, input().split())) ans = solve(N, D) print(len(ans)) if __name__ == "__main__": main()
C問題
問題の概要
文字列が与えられる。の文字目から文字目までからなる部分文字列において、同じ文字が2つ隣り合う箇所はいくつあるか、という個の質問に答えよ。
問題の考察
2つ隣り合う箇所の累積和を事前に求めておくことで、各質問にで答えます。
コード
from itertools import pairwise def solve(N, Q, S, LR): X = [0, 0] for s1, s2 in pairwise(S): X.append(X[-1] + int(s1 == s2)) ans = [] for left, right in LR: z = X[right] - X[left] ans.append(z) return ans def main(): N, Q = map(int, input().split()) S = input() LR = [list(map(int, input().split())) for _ in range(Q)] ans = solve(N, Q, S, LR) print(*ans) if __name__ == "__main__": main()
D問題
問題の概要
A,B,Cの3種類の文字からなる文字列が与えられる。連続な部分文字列としてABCを含む限りから削除するというのを繰り返すとき、最終的なを出力せよ。
問題の考察
の先頭の文字から順にスタックに積んでいき、スタックの後ろ3文字がABCだった場合はスタックから3文字分だけpopします。Pythonの場合、スライスを使って後ろ3文字を調べることで、現在のスタックのサイズが3未満だったとしてエラーなく判定できます。
コード
def solve(S): lifo = [] for s in S: lifo.append(s) S3 = "".join(lifo[-3:]) if S3 == "ABC": lifo.pop() lifo.pop() lifo.pop() return "".join(lifo) def main(): S = input() ans = solve(S) print(ans) if __name__ == "__main__": main()
E問題
問題の概要
頂点辺の重み付き単純連結無向グラフと正整数が与えられる。このグラフの全域木に対して、含まれる辺の重みの総和をで割った余りをその全域木のコストとするとき、このグラフの全域木のコストの最小値を求めよ。
問題の考察
制約より最大で頂点数が8で辺数が28であることから、このグラフから作れる全域木の数は高々と分かるため、全探索しても間に合いそうです。辺の集合から個の辺を取り出す組合せを全探索します。各組合せでは、UnionFindを使ってサイズがであるかを調べることで、全域木になっているかを判定します。
コード
from itertools import combinations from atcoder.dsu import DSU def solve(N, M, K, edges): ans = float("inf") for sub_edges in combinations(edges, N - 1): uf = DSU(N) total = 0 for u, v, w in sub_edges: uf.merge(u, v) total += w total %= K if uf.size(0) == N: ans = min(ans, total) return ans def main(): N, M, K = map(int, input().split()) edges = [] for _ in range(M): u, v, w = map(int, input().split()) u -= 1 v -= 1 edges.append((u, v, w)) ans = solve(N, M, K, edges) print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC326を解く
はじめに
abc326のA,B,C,E問題をPython3で解きます。
A問題: 2UP3DOWN
問題の概要
ビルの階から階へ移動するとき、2階分までの上り、または、3階分までの下りであれば階段を使い、そうでないときはエレベータを使う。階段で移動するか判定せよ。
問題の考察
が範囲内に収まっているかで判定します。
コード
def main(): x, y = map(int, input().split()) ans = "Yes" if -3 <= (y - x) <= 2 else "No" print(ans) if __name__ == "__main__": main()
B問題: 326-like Numbers
問題の概要
3桁の整数について、百の位のかずと十の位の数の積が一の位の数に等しいものを326-like numberと呼ぶ。整数以上の最小の326-like numberを求めよ。
問題の考察
制約を見ると分かるように326-like numberの最大値は919ですので、]の範囲で順に探索します。各位の数を取り出すには整数を文字列に変換すると楽です。
コード
def solve(N): for i in range(N, 920): a, b, c = map(int, list(str(i))) if a * b == c: return i return -1 def main(): N = int(input()) ans = solve(N) print(ans) if __name__ == "__main__": main()
C問題: Peak
問題の概要
数直線上の座標にプレゼントが置かれている。ある座標を選ぶと半開区間にあるプレゼントを全て獲得できる。適切にを選ぶと最大でいくつのプレゼントを獲得できるか。
問題の考察
配列をソートした上で、を各で全探索しつつ高速に獲得できるプレゼント数をカウントします。二分探索で未満の数と未満の数をそれぞれ求めて差を計算すると、半開区間にあるプレゼント数をでカウントできます。前処理としてのソート、および、全探索と二分探索により、の計算量でプレゼント数の最大値を計算できました。
コード
from bisect import bisect_left def solve(N, M, A): ans = 0 for a in A: x = bisect_left(A, a + M) - bisect_left(A, a) ans = max(ans, x) return ans def main(): N, M = map(int, input().split()) A = list(map(int, input().split())) A = sorted(A) ans = solve(N, M, A) print(ans) if __name__ == "__main__": main()
E問題: Revenge of "The Salary of AtCoder Inc."
問題の概要
そのままですので、略します。
問題の考察
をダイスでが出ているときの給料の期待値とします。ダイスでが出ているときにさらに手順を踏むと、次のダイスがからまでのときはそれぞれの確率でが支給され、からまでのときはそれぞれの確率でが支給されます。
上記の式について、の状態からの状態まで計算していけば、のときの給料の期待値が計算できます。なお、高速化のために式の最後の総和では累積和を用います。これによって、計算量で期待値が求まります。なお、答えをで計算する必要があるため、での割り算はの掛け算で実現します。
コード
def solve(N, A, MOD=998244353): A = [0] + A dp = [0] * (N + 1) ep = [0] * (N + 2) modinv_N = pow(N, -1, MOD) for x in range(N, -1, -1): dp[x] = A[x] dp[x] += ep[x + 1] * modinv_N % MOD dp[x] %= MOD ep[x] = dp[x] + ep[x + 1] ep[x] %= MOD ans = dp[0] return ans def main(): N = int(input()) A = list(map(int, input().split())) ans = solve(N, A) print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC325を解く
はじめに
abc325のA,B,C,D問題をPython3で解きます。
A問題: Takahashi san
問題の概要
与えられた名字と名前に対して、名字の方に敬称(san)を付けよ。
問題の考察
Pythonで文字列のフォーマットをいじるのに便利なf-stringを使って敬称を付けます。
コード
def main(): S, T = input().split() ans = f"{S} san" print(ans) if __name__ == "__main__": main()
B問題: World Meeting
問題の概要
ある国の拠点の時刻は世界標準時で0時のときに時で、その拠点には人の社員が所属している。各拠点の社員は、現地時間で09:00-18:00の時間帯のときのみ会議に参加できる。適切な開始時刻を選んで1時間の会議を設定するとき、会議の参加人数は最大で何人になるか。
コード
def solve(N, W, X): ans = 0 for t in range(24): total = sum(w for w, x in zip(W, X) if 9 <= (t + x) % 24 < 18) ans = max(ans, total) return ans def main(): N = int(input()) W = [] X = [] for _ in range(N): w, x = map(int, input().split()) W.append(w) X.append(x) ans = solve(N, W, X) print(ans) if __name__ == "__main__": main()
C問題: Sensors
問題の概要
のマス目にセンサが配置されている。上下左右斜めで隣接しているセンサ同士は、連動して動作する一纏まりのセンサ群として見なせる。センサ群は何個あるか。
問題の考察
幅優先探索やUnionFindで解くことができますが、ここではUnionFindで解きます。上下左右斜めで隣接しているセンサをUnionFindで同じ集合として結合し、最後に各集合の親がセンサか否かでセンサ群を数えます。
UnionFindのソースコードは、こちらから引用しました(https://note.nkmk.me/python-union-find/)。いつもお世話になっております。
コード
from collections import defaultdict class UnionFind: def __init__(self, n): self.n = n self.parents = [-1] * n def find(self, x): if self.parents[x] < 0: return x else: self.parents[x] = self.find(self.parents[x]) return self.parents[x] def union(self, x, y): x = self.find(x) y = self.find(y) if x == y: return if self.parents[x] > self.parents[y]: x, y = y, x self.parents[x] += self.parents[y] self.parents[y] = x def size(self, x): return -self.parents[self.find(x)] def same(self, x, y): return self.find(x) == self.find(y) def members(self, x): root = self.find(x) return [i for i in range(self.n) if self.find(i) == root] def roots(self): return [i for i, x in enumerate(self.parents) if x < 0] def group_count(self): return len(self.roots()) def all_group_members(self): group_members = defaultdict(list) for member in range(self.n): group_members[self.find(member)].append(member) return group_members def __str__(self): return "\n".join(f"{r}: {m}" for r, m in self.all_group_members().items()) def solve(H, W, S): uf = UnionFind((H + 2) * (W + 2)) for h in range(1, H + 1): for w in range(1, W + 1): if S[h][w] == "#": u = h * (W + 2) + w for dh in range(-1, 2): for dw in range(-1, 2): hh = h + dh ww = w + dw if S[hh][ww] == "#": v = hh * (W + 2) + ww uf.union(u, v) ans = 0 for r in uf.roots(): h = r // (W + 2) w = r % (W + 2) ans += int(S[h][w] == "#") return ans def main(): H, W = map(int, input().split()) S = [] S.append("." * (W + 2)) for _ in range(H): line = "." + input() + "." S.append(line) S.append("." * (W + 2)) ans = solve(H, W, S) print(ans) if __name__ == "__main__": main()
D問題: Printing Machine
問題の概要
商品は、今から秒後に印字機に入り、その秒後に印字機から出る。印字機は、中にある商品に対して一瞬で印字することができるが、印字の度に1秒間のチャージ時間を必要とする。印字する対象の商品と印字のタイミングを適切に選んだとき、最大で何個の商品に印字できるか。
問題の考察
印字機に入っている商品の中で最も出が早いものから印字する貪欲法でよさそうですが、が大きいため、印字機への入りと出を毎秒シミュレーションする方法だと計算量的に間に合いません。
時間でのシミュレーションではなく、商品でのシミュレーションによる貪欲法を考えます。まず、印字機に入る時刻で管理された商品用のヒープ(in_heap)と、印字機を出る時刻で管理された商品用のヒープ(out_heap)を持ちます。①in_heap先頭の商品と同じ入り時刻の商品全てをout_heapに入れ、現時刻をに更新します。②out_heapの先頭の商品の出時刻が以上であるときは印字してチャージし()、そうでないときは印字しない(できない)、というのをin_heap先頭の商品の入り時刻がより大きいうちは繰り返します。①と②をin_heapが空になるまで繰り返しせば、の計算量で解けました。
コードでは、入りと出の時刻が十分大きい商品を番兵としてin_heapに入れており、これによってin_heapが空になることはないために条件分岐がシンプルになります。
コード
from heapq import heappop, heappush def solve(N, T, D): in_heap = [] for t, d in zip(T, D): heappush(in_heap, (t, d)) heappush(in_heap, (10000000000000000000, 10000000000000000000)) out_heap = [] ans = 0 while len(in_heap) > 1: tau = in_heap[0][0] while in_heap[0][0] <= tau: t, d = heappop(in_heap) heappush(out_heap, t + d) while out_heap and tau < in_heap[0][0]: if tau <= out_heap[0]: heappop(out_heap) ans += 1 tau += 1 else: heappop(out_heap) return ans def main(): N = int(input()) T = [] D = [] for _ in range(N): t, d = map(int, input().split()) T.append(t) D.append(d) ans = solve(N, T, D) print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC324を解く
はじめに
abc324のA,B,C,D問題をPython3で解きます。
A問題: Same
問題の概要
与えられる整数が全て等しいか答えよ。
問題の考察
集合のサイズが1かを調べます。
コード
def main(): N = int(input()) A = list(map(int, input().split())) ans = "Yes" if len(set(A)) == 1 else "No" print(ans) if __name__ == "__main__": main()
B問題: 3-smooth Numbers
問題の概要
正の整数について、を満たす整数が存在するか答えよ。
問題の考察
をで割れるだけ割ったときにとなれば、そのようなが存在しています。
コード
def solve(N): n = N while n % 2 == 0: n //= 2 while n % 3 == 0: n //= 3 return n == 1 def main(): N = int(input()) ans = "Yes" if solve(N) else "No" print(ans) if __name__ == "__main__": main()
C問題: Error Correction
問題の概要
個の文字列が文字列とほぼ等しいかを判定せよ(ほぼ等しいの意味は問題文を参照)。
問題の考察
文字列と文字列の長さが等しい場合、1つ差のある場合、それ以外の3つに場合分けて考えます。長さが等しい場合、先頭から順に見て違う文字同士の数が1以下ならほぼ等しいと分かります。1つ差の場合、先頭から順に見て違う文字があったらそれ以降が全て同じ文字ならばほぼ等しいと分かります。それ以外の場合、明らかにほぼ等しくなることはないです。個の文字列の入力と先頭から1文字ずつ見ていく処理があるため、計算量はで解けました。
コード
def solve(N, TT, Ss): def is_valid(S1, S2): if len(S1) > len(S2): S1, S2 = S2, S1 if len(S1) == len(S2): return sum([s != tt for s, tt in zip(S1, S2)]) <= 1 elif len(S1) + 1 == len(S2): for i, (s1, s2) in enumerate(zip(S1, S2)): if s1 != s2: return S1[i:] == S2[i + 1 :] return True return False ans = [] for i, S in enumerate(Ss, 1): if is_valid(S, TT): ans.append(i) return ans def main(): N, TT = input().split() N = int(N) Ss = [input() for _ in range(N)] ans = solve(N, TT, Ss) print(len(ans)) print(*ans) if __name__ == "__main__": main()
D問題: Square Permutation
問題の概要
長さの文字列を並び替えた結果を10進数の整数として解釈したもののうち、平方数となるようなものはいくつあるか答えよ。
問題の考察
と小さくはあるものの、並び替えた整数を全て作るのにの計算量だけ掛かるため、この方針だと解けないと分かります。
Wikipediaの平方数のページにあった平方数の数列を見ると、平方数の数は明らかに少ないと分かるため、先に平方数を列挙し、その後に文字列で各平方数を作れるかを判定するのがよさそうです。平方数を列挙するには、からまでの整数を二乗すれば得られます。各平方数を作れるかは、各平方数をゼロパディングした桁の10進数と見たときに各桁に現れる整数の頻度が、文字列と同じか調べれば判定できます。平方数を列挙するのに、各平方数を作れるかの判定にだけ掛かるため、全体での計算量で解けました。
コード
from collections import Counter from itertools import count, takewhile def solve(N, target_digits): target_hist = Counter(target_digits) MAX = pow(10, N) squares = [i * i for i in takewhile(lambda x: x * x < MAX, count())] ans = 0 for s in squares: digits = list(f"{s:0{N}}") hist = Counter(digits) ans += int(hist == target_hist) return ans def main(): N = int(input()) S = input() ans = solve(N, list(S)) print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC323を解く
はじめに
abc323のA,B,C,D問題をPython3で解きます。
A問題: Weak Beats
問題の概要
16bitの数値が与えられる。MSB(最上位ビット)から見た2bit目、4bit目、…、16bit目といった偶数目のビット全てが0かを答えよ。
コード
def main(): S = int(input(), 2) ans = "No" if S & 0b0101010101010101 else "Yes" print(ans) if __name__ == "__main__": main()
B問題: Round-Robin Tournament
問題の概要
からまでの番号が付いたプレイヤー人での総当たり戦の結果が与えられる。勝利数が多い方が順位は上になり、勝利数が同じ場合はプレイヤー番号が小さい方が順位は上になる。順位が高い順にプレイヤー番号を答えよ。
問題の考察
勝利数とプレイヤー番号のタプルを作ってソートします。
コード
def solve(N, Ss): wins = [0] * N for i, S in enumerate(Ss): for s in S: if s == "o": wins[i] += 1 X = [(-w, i) for i, w in enumerate(wins, 1)] X = sorted(X) ans = [i for _, i in X] return ans def main(): N = int(input()) Ss = [input() for _ in range(N)] ans = solve(N, Ss) print(*ans) if __name__ == "__main__": main()
C問題: World Tour Finals
問題の概要
人のプレイヤーそれぞれについて、点数な問の問題をある時刻に解いた結果が与えられる。各プレイヤーについて、その時刻の最高点を上回るのに必要な解くべき問題数の最小値を求めよ。
問題の考察
点数の高い順に問題をソートしておき、まだ解いていない問題から加算していくことで、最高点を超えるのに必要な問題数を計算します。
コード
def solve(N, M, A, Ss): scores = [ i + sum(A[j] for j, s in enumerate(S) if s == "o") for i, S in enumerate(Ss, 1) ] target = max(scores) X = [(a, i) for i, a in enumerate(A)] X = sorted(X, reverse=True) ans = [] for i, (S, score) in enumerate(zip(Ss, scores)): if score == target: ans.append(0) else: n = 0 for a, j in X: if S[j] == "x": score += a n += 1 if score > target: ans.append(n) break return ans def main(): N, M = map(int, input().split()) A = list(map(int, input().split())) S = [input() for _ in range(N)] ans = solve(N, M, A, S) print(*ans) if __name__ == "__main__": main()
D問題: Merge Slimes
問題の概要
種類のサイズのスライムがおり、サイズのスライムは匹いる。同じサイズのスライムが2匹いるとき、合成することでその2匹は消滅して代わりに倍のサイズのスライム1匹になる。最小でスライムは何匹になるか。
問題の考察
小さいサイズから順に合成していけばよさそうです。ヒープに初期値として種類のサイズを入れておき、小さいサイズから取り出しつつ合成したら倍のサイズをヒープに入れます。
コード
from collections import Counter from heapq import heappop, heappush def solve(N, S, C): cnts = Counter() for s, c in zip(S, C): cnts[s] += c heap = [] for s in S: heappush(heap, s) while heap: s = heappop(heap) if cnts[s] > 1: cnts[2 * s] += cnts[s] // 2 cnts[s] %= 2 heappush(heap, 2 * s) return sum(cnts.values()) def main(): N = int(input()) S, C = list(zip(*[list(map(int, input().split())) for _ in range(N)])) ans = solve(N, S, C) print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC322を解く
はじめに
ABC322のA,B,C,D問題をPython3で解きます。
A問題: First ABC 2
問題の概要
文字列の中で文字列ABCが部分文字列として初めて現れる位置を答えよ。現れない場合は-1を出力せよ。
問題の考察
pythonの文字列クラスにあるfindメソッドを使うと楽です。
コード
def solve(S, TARGET="ABC"): return S.find(TARGET) def main(): _ = input() S = "@" + input() ans = solve(S) print(ans) if __name__ == "__main__": main()
B問題: Prefix and Suffix
問題の概要
文字列が与えられる。がの接頭語であるかないか、がの接尾語であるかないかで計4パターン存在する。どのパターンかを答えよ。
問題の考察
接頭語か否かの判定結果(is_head)と接尾語か否かの判定結果(is_tail)を作って、その二つを元にどのパターンかを返答します。
コード
def solve(N, M, S, T): is_head = int(S == T[:N]) is_tail = int(S == T[-N:]) ans = ((1 - is_head) << 1) | (1 - is_tail) return ans def main(): N, M = map(int, input().split()) S = input() T = input() ans = solve(N, M, S, T) print(ans) if __name__ == "__main__": main()
C問題: Festival
問題の概要
日間のお祭りにおいて、日目に花火が上がる。に対して、日目以降で初めて花火が上がるのは日目から数えて何日後か求めよ。
問題の考察
各日目について二分探索することで初めて花火が上がる日を調べます。結果として、計算量はとなります。
コード
from bisect import bisect_left def solve(N, M, A): ans = [] for i in range(1, N + 1): j = bisect_left(A, i) ans.append(A[j] - i) return ans def main(): N, M = map(int, input().split()) A = list(map(int, input().split())) ans = solve(N, M, A) print(*ans) if __name__ == "__main__": main()
D問題: Polyomino
問題の考察
この手の問題は集合の論理和と論理積を使うと実装が楽になる印象があります。まずは、ポリオミノがあるマスの集合でもって各ポリオミノを表します。次に、ポリオミノについて平行移動16パターンと回転4パターンを全探索する関数を作ります。最後にその関数を3つのポリオミノにそれぞれ適用し、ポリオミノ同士の論理積が空集合なこと、および、3つのポリオミノの論理和が4x4のグリッドになっていることを判定します。
コード
def simplify(P): min_j = min([j for j, _ in P]) min_k = min([k for _, k in P]) Q = set([(j - min_j, k - min_k) for j, k in P]) return Q def rotate(P): Q = set([(-k, j) for j, k in P]) return Q def shift(P, dj, dk): Q = set([(j + dj, k + dk) for j, k in P]) return Q def move(P): Q = P for _ in range(4): Q = simplify(Q) for dj in range(4): for dk in range(4): R = shift(Q, dj, dk) yield R Q = rotate(Q) def is_valid(P1, P2, P3): if P1 & P2 or P2 & P3 or P1 & P3: return False TARGET = set([(j, k) for j in range(4) for k in range(4)]) result = P1 | P2 | P3 return result == TARGET def solve(P1, P2, P3): for Q1 in move(P1): for Q2 in move(P2): for Q3 in move(P3): if is_valid(Q1, Q2, Q3): return True return False def main(): Ps = [] for _ in range(3): P = set() for j in range(4): line = input() for k, p in enumerate(line): if p == "#": P.add((j, k)) Ps.append(P) ans = "Yes" if solve(*Ps) else "No" print(ans) if __name__ == "__main__": main()
【AtCoder】PythonでABC321を解く
はじめに
ABC321のA,B,C,D問題をPython3で解きます。
A問題: 321-like Checker
問題の概要
正整数の桁が上から順に狭義単調減少になっているとき、その正整数を321-like Numberと呼ぶ。与えられた正整数が321-like Numberかを判定せよ。
問題の考察
itertoolsのpairwiseを使うことで隣り合う2つの要素を取り出せるため、これを使って単調減少しているかを判定します。
コード
from itertools import pairwise def solve(N): return all(s1 > s2 for s1, s2 in pairwise(N)) def main(): N = input() ans = "Yes" if solve(N) else "No" print(ans) if __name__ == "__main__": main()
B問題: Cutoff
問題の概要
ラウンドからなる試験の最終結果は、最高スコアのラウンドと最低スコアのラウンドを除いたラウンド分のスコアの合計となる。ラウンドまでが終了して各ラウンドのスコアがであるとき、最終結果を以上にするために最終ラウンドで取るべきスコアの最小値を求めよ。
問題の考察
最終ラウンドのスコアを]の範囲で全探索します。
コード
def solve(N, X, A): ans = float("inf") for a in range(101): A.append(a) total = sum(A) - min(A) - max(A) if total >= X: ans = min(ans, a) A.pop() if ans == float("inf"): ans = -1 return ans def main(): N, X = map(int, input().split()) A = list(map(int, input().split())) ans = solve(N, X, A) print(ans) if __name__ == "__main__": main()
C問題: 321-like Searcher
問題の概要
番目に小さい321-like Numberを求めよ。
問題の考察
最も大きい321-like Numberは9876543210であること、および、321-like Numberである条件が厳しいことから、321-like Numberを満たす正整数の数は少なそうなため、全てを列挙しても大丈夫そうです。dequeを321-like Numberである1から9で初期化し、①dequeの先頭から正整数を取り出す、②その正整数から作れる321-like Numberをdequeの末尾に入れる、という①②をdequeが空になるまで繰り返します。
コード
from collections import deque def solve(K): fifo = deque(range(1, 10)) A = [] while fifo: x = fifo.popleft() A.append(x) for d in range(x % 10): xx = 10 * x + d fifo.append(xx) return A[K] def main(): K = int(input()) ans = solve(K - 1) print(ans) if __name__ == "__main__": main()
D問題: Set Menu
問題の概要
価格な種類の主菜と価格な種類の副菜がある。主菜と副菜からなる定食を提供するとき、その価格はとなる。通りある定食についての価格の総和を求めよ。
問題の考察
主菜を全探索するとき、となる副菜ととなる副菜の個数が高速に分かればよさそうです。事前に副菜を価格でソートしておき、副菜の価格の累積和を取っておきます。ある主菜について二分探索でとなる副菜の数を計算すれば、ある主菜と全副菜との価格の和は累積和を使ってで計算できます。副菜のソートに、各主菜での副菜の数の計算にだけ掛かるため、全体としての計算量で価格の総和を求められました。
コード
from bisect import bisect_left from itertools import accumulate def solve(N, M, P, A, B): B = sorted(B) accB = list(accumulate(B, initial=0)) ans = 0 for a in A: j = bisect_left(B, P - a) ans += (j * a + accB[j]) + (M - j) * P return ans def main(): N, M, P = map(int, input().split()) A = list(map(int, input().split())) B = list(map(int, input().split())) ans = solve(N, M, P, A, B) print(ans) if __name__ == "__main__": main()