Python初心者のためのABC207
A問題
お好きなやり方でどうぞ.
# 解法1 A,B,C = map(int,input().split()) print(max(A+B,B+C,C+A)) # 解法2 L = list(map(int,input().split())) print(sum(L)-min(L)) # 解法3 L = sorted(list(map(int,input().split()))) print(L[1]+L[2])
B問題
操作回数を とすると, すなわち が成り立つ必要があります.
- のとき,条件をみたす は存在しません.
- のとき,条件をみたす最小の は です.
を用いれば,整数同士の計算のみで完結するため誤差の心配はありません.
A,B,C,D = map(int,input().split()) print((A-1)//(C*D-B)+1 if C*D-B>0 else -1)
C問題
開区間はちょっとだけ幅を狭めた閉区間だと思って処理すれば良いです.制約が と小さいため,すべての区間のペアについて条件を満たすか確認する の全探索が可能です.
N = int(input()) D = [] for _ in range(N): t,l,r = map(int,input().split()) l += 0.1 if t in [3,4] else 0 r -= 0.1 if t in [2,4] else 0 D.append([l,r]) ans = 0 for i in range(N): li,ri = D[i] for j in range(i+1,N): lj,rj = D[j] if li<=rj and lj<=ri: ans += 1 print(ans)
D問題
ここでは,複素数を用いた解法を紹介します.
Step1:特殊ケースの例外処理①
のとき,平行移動のみで と は必ず一致します.以降は の場合を考えます.
Step2:重心に注目した平行移動
公式解説にある通り, と が一致するならば両者の重心も一致しており,回転移動では重心の位置は変わりません.よって, と の重心が一致するような平行移動が必要であり,かつ,それ以外の平行移動は許されないことが分かります.両者の重心が原点に一致するように平行移動させることで,後は回転移動のみを考えれば良いことになります.
Step3:特殊ケースの例外処理②
の代表点( とします)を一つ選び, に含まれる点のうち と一致させる点 ( とします)を決めれば,回転角度は一意に決まります.しかし, が原点と一致する場合には,対応する は原点以外ありえません.そのため, か のいずれか一方にのみ原点が含まれる場合には,その時点で と を一致させることは不可能であると判定できます.また, と の両方に原点が含まれる場合には,それぞれから原点を削除した残りの 点について一致するかどうかを考えれば良いです.後々の操作を考慮して,このようなケースに対応する処理を加えておきます.
Step4:複素数を用いた回転移動
複素数平面を考えることで各点を複素数に対応させます.すなわち, とします.
の代表点 を適当に選びます.なお,
- Step1によって が保証されている
- Step3によって の長さが に減少している可能性がある
を考慮すると,この時点で の長さは必ず 以上であるため, が空だったことによるREの心配はありません.
に対応する の点 を全探索します. を に一致させるような回転移動は, として と一意に定まります.ただし,これが回転移動であるためには が成り立つ必要があります.なお,
- Step3によって が保証されている
を考慮すると,ゼロ除算によるREの心配はありません. が決まれば,後は各 に対して となるかを判定すれば良いです. と それぞれについて同一の点は含まれないという仮定から,対応する点が重複する心配はありません.
※ 実装上の注意
はPythonの複素数型を用いて complex(x,y)
と表されます. を求める際に複素数同士の割り算を行うため,誤差に注意する必要があります.特に, および の処理では完全一致 が求められますが,条件を緩和した などに置き換えてやると良いです.
この解法の計算量は,Step4における の全探索がボトルネックとなり です.
N = int(input()) S = [list(map(int,input().split())) for _ in range(N)] T = [list(map(int,input().split())) for _ in range(N)] # Step1 if N==1: print('Yes') exit() # Step2 cx = 0 cy = 0 for x,y in S: cx += x cy += y cx /= N cy /= N S = [complex(x-cx,y-cy) for x,y in S] cx = 0 cy = 0 for x,y in T: cx += x cy += y cx /= N cy /= N T = [complex(x-cx,y-cy) for x,y in T] # Step3 if 0 in S and 0 not in T or 0 not in S and 0 in T: print('No') exit() if 0 in S and 0 in T: S.remove(0) T.remove(0) # Step4 s0 = S[0] eps = 1e-10 for t0 in T: theta = t0/s0 if abs(abs(theta)-1)>eps: continue ok = True for s in S: ok_s = False for t in T: if abs(theta*s-t)<eps: ok_s = True break if not ok_s: ok = False break if ok: print('Yes') exit() print('No')
E問題
Step1:解法に目処を立てる
「数列が与えられていて という制約なので, の動的計画法(DP)で解きましょう」と言いたいところですが,さすがに乱暴すぎるので思考を少し説明します.DPの本質は「ある問題を解くときに,より規模の小さい問題の答えを利用する」という点にあります.今回の場合,
- 数列 の第 項までを考えようとしたときに,第 項まで,第 項まで,…,第 項までの結果が使えそうな気がする
という直感からDPを選択します.実際にこれが合っているかどうかは以降の考察によって確認されることになるのですが,とりあえずは直感を信じて考察を進めてみます.
Step2:状態数・遷移の定式化
DPの問題が解けるかどうかは,きちんと状態数を決められるかどうかが最初のポイントです.今回の場合,とりあえず問題を見たときに自然に思いつく状態数は
- を見ていることを表す
- に分割していることを表す
の2つがあります.さらに, という制約から計算量は であることも予想がついているので,状態数は高々2つです.よって,おそらく上記の2つが正しい状態数になっていそうです.以降では,
- : を に分割する場合の数
を考えることにします.
次に,状態の遷移を考えましょう.簡単な例として, に対する を考えてみます. の末尾から連続する何項かが に分割されているはずです. に含まれる数の和は の倍数でなければならないことから, として , の2通りが考えられます.
という条件の下, を に分割する場合の数は,残りの を に分割する場合の数 に等しいです.また, という条件の下, を に分割する場合の数は,残りの を に分割する場合の数 に等しいです.よって, と計算できます.
この結果を一般化すると,
が成り立ちます.なお, は が の倍数であるような を表します.以上により,問題をDPとして定式化することができました.
Step3:DPの高速化
を計算するために足し合わせる は最大で 通りあるため,状態数 の各遷移が の計算量を要し,全体では となってTLEしてしまいます.状態数を減らすことは難しそうなので,各遷移の計算量を に抑えられないでしょうか.
ここで,連続部分列の要素和に対する典型テクニックである
と表せることを用います.なお,, です.これにより, は と同値であることが分かります.よって,
- : のうち, を で割った余りが であるような についての の和
を用意すれば, と で計算できます.問題の ですが, を固定すれば の小さい順に で更新していくことが出来ます.
Step4:答えの導出
以上により,計算量 のDPを構成することができました.求めたい答えは です.計算を行うたびに で割った余りを求めることを忘れないようにしましょう.
N = int(input()) A = list(map(int,input().split())) MOD = 10**9+7 dp = [[0]*(N+1) for _ in range(N+1)] dp[0][0] = 1 for j in range(1,N+1): fact = [0]*j fact[0] = dp[0][j-1] S = 0 for i in range(1,N+1): S += A[i-1] S %= j dp[i][j] = fact[S] fact[S] += dp[i][j-1] fact[S] %= MOD ans = 0 for j in range(1,N+1): ans += dp[N][j] ans %= MOD print(ans)