AtCoder Beginner Contest 166(ABC166) A~F解説

AtCoder Beginner Contest 166(ABC166)のA~Fまでの解説です。
コンテスト中はA~Fまでを解き(98:20+3WA)、初の全完を達成しました*(前回に引き続き青パフォを出せ、レートを少し伸ばすことができました)
問題のアドレスは以下になります↓
https://atcoder.jp/contests/abc166/
公式の解説(Editorial)は以下になります↓
https://img.atcoder.jp/abc166/editorial.pdf

A問題:

問題文の通り、入力が"ABC"であれば"ARC"を、入力が"ARC"であれば"ABC"を出力すればよいです。

B問題:

制約が小さいので、問題文の通りに1人目からN人目までの人がそれぞれいくつのお菓子を買ったかを数え、その後1つもお菓子を買っていない人の数を数えることでこの問題を解くことができます(問題文に書いてあるとおりの処理を行うだけなのですが、処理が二段階に分かれており少し面倒な問題になっていますのでサンプルコードを掲載しておきます)。

サンプルコード(Python):
https://atcoder.jp/contests/abc166/submissions/12712622

n,k=map(int,input().split())
cnt=[0]*(n+1) #i人目の人が所持しているお菓子の数を数える
for _ in range(k): #それぞれのお菓子について、そのお菓子を買った人のお菓子の所持数を1増やす
 tmp=int(input())
 arr=list(map(int,input().split()))
 for val in arr:
   cnt[val]+=1
ans=0
for i in range(1,n+1):
 if cnt[i]==0: #すべてのお菓子を見終えたあと、1つもお菓子を持っていない人の数が答え
   ans+=1
print(ans)

C問題:

一見するとN個の展望台についてM個の条件を確認する必要がありO(NM)かかるように見えますが、実際にはN個の展望台についてトータルで2M回条件を確認することになるので、計算量はO(N+M)となります(∵各条件を見るのはその条件のどちらかにi番目の展望台が含まれているときのみで、M個の条件は2M個の展望台を持つので、計算量は上記の通りとなります)。
よって、N個の展望台を見るときに、トータルで2M回条件を確認するような実装をすればよいです。
これは例えばすべてのM本の道を隣接リストとして持っておき、i番目の展望台を見るときにはi番目の展望台と辺で結ばれたすべての展望台について高さを比較することで実現できます。
ただし、i番目の展望台が良い展望台となるのは他のすべての展望台より高さが真に大きいときであることに注意します。
以上によりこの問題を解くことができました。

サンプルコード(Python):
https://atcoder.jp/contests/abc166/submissions/12730128

n,m=map(int,input().split())
arr=[0]+list(map(int,input().split()))
g=[[] for _ in range(n+1)]
for _ in range(m):
 a,b=map(int,input().split())
 g[a].append(b)
 g[b].append(a)
ans=0
for v in range(1,n+1):
 for u in g[v]: #i番目の展望台から1本の道を通っていけるすべての展望台と高さを比較する
   if arr[u]>=arr[v]:
     break
 else: #i番目の展望台の高さが他のすべての展望台の高さより真に大きければ答えを1増やす
   ans+=1
print(ans)

D問題:

直感的に、A,Bが十分大きいとき、A^5-B^5=Xとなることはなさそうです。
これから、具体的に1<=A^5-B^5<=10^9を満たす最大のAを、正と負の場合のそれぞれについて考えてみます。
Aを固定したときabs(A^5-B^5)が最小となるのはB=A-1のときなので、これより、Aが正のときA=119(,B=118)が1<=A^5-B^5<=10^9を満たす最大のAとなり、Aが負のときA=-118(,B=-119)が1<=A^5-B^5<=10^9を満たす最大のAとなります。
したがって、考えるべきAの範囲は-118<=A<=119のみであることが分かります。
同様の計算により、考えるべきBの範囲が-119<=B<=118のみであることが分かります。
この条件を満たす全ての(A,B)の組は238^2=56644通りしかないので、この問題を解くことができました(十分大きなA,Bについて、まずi^5,(-i)^5(0<=i<=A)をO(A)で列挙し、それらの値と底の組を連想配列に追加し、その後i^5+Xまたは(-i)^5+Xが連想配列に含まれるかをO(BlogA)で判定することもできます、またメタな話になりますが、A^5-B^5=Xを満たすA,Bがあまりに大きい(64bitを超える)と多倍長整数を用いないと解けなくなってしまうので、A,Bとしては(2^63)^(1/5)までを考えれば十分です)。

サンプルコード(Python):
https://atcoder.jp/contests/abc166/submissions/12806330

x=int(input())
for a in range(-118,120):
 for b in range(-119,119):
   if a**5-b**5==x:
     print(a,b)
     exit()

E問題:

問題文を整理すると、各(i,j) (i<j)について、j-i=Ai+Ajを満たす(i,j)の個数を数えればよいことが分かります。
これを愚直に全探索するとO(N^2)となり間に合いませんが、j-i=Aj+Ai⇔j-Aj=Ai+iより変数が独立しているので、jを1からNまで動かしていき、すでに見たi(<j)についてj-Ajの個数を数え、その後Aj+jを付け加えればよいです。
個数の管理には連想配列を用いるのが簡単で、以上によりこの問題をO(NlogN)で解くことができました(。

サンプルコード(Python):
https://atcoder.jp/contests/abc166/submissions/12806889

import collections

n=int(input())
arr=list(map(int,input().split()))
ans=0
cnt=collections.defaultdict(int)
for i in range(1,n+1):
 ans+=cnt[i-arr[i-1]] #すでに見た要素について、i-Aiと等しいものの個数を数える
 cnt[i+arr[i-1]]+=1 #i+Aiを付け加える
print(ans)

F問題:

ある時点で選ばれた2要素について、その操作の終了後にどちらか一方の要素が0になることがなければ、どちらの要素を増加させても次の操作を行うことができます(∵3要素のうち少なくとも2要素が1以上であれば必ず次の操作を行うことができます)。
またどちらか一方の値が0であればその要素を増加させるしかなく、どちらの値も0であればその時点で操作は終了します。
よって、ある時点で選ばれた2要素について、どちらの値も1のとき、すなわちその操作の終了後にどちらか一方の要素が0になるときのみ、どのように操作を行うのが最善かを考える必要があります。
次に行う操作と共通する要素が1つしかないとき、その要素を増加させる必要があります。
また、次に行う操作と共通要素が2つある、もしくは最後の操作であるとき、どちらの要素を増加させても次の操作を行うことができます。
以上により、すべての場合についてどのように操作を行えばよいかを決定できたので、この問題をO(N)で解くことができました。

サンプルコード(Python):
https://atcoder.jp/contests/abc166/submissions/12807538

n,a,b,c=map(int,input().split())
arr=[input() for _ in range(n)]
ans=[]
for i in range(n):
 if arr[i]=='AB':
   if a==1 and b==1: #どちらも1のときは次の操作によりどちらの要素を増やすかを決定する
     if i==n-1 or arr[i+1]=='AB' or arr[i+1]=='AC':
       a+=1
       b-=1
       ans.append('A')
     elif arr[i+1]=='BC':
       a-=1
       b+=1
       ans.append('B')
   elif a==0 and b==0: #どちらも0のときはそこで操作が終了する
     print('No')
     break
   else: #少なくとも一方が0でないときは、大きい方の値を減らし、小さい方の値を増やせばよい
     if a>=b:
       a-=1
       b+=1
       ans.append('B')
     else:
       a+=1
       b-=1
       ans.append('A')
 elif arr[i]=='AC':
   if a==1 and c==1: #どちらも1のときは次の操作によりどちらの要素を増やすかを決定する
     if i==n-1 or arr[i+1]=='AC' or arr[i+1]=='AB':
       a+=1
       c-=1
       ans.append('A')
     elif arr[i+1]=='BC':
       a-=1
       c+=1
       ans.append('C')
   elif a==0 and c==0: #どちらも0のときはそこで操作が終了する
     print('No')
     break
   else: #少なくとも一方が0でないときは、大きい方の値を減らし、小さい方の値を増やせばよい
     if a>=c:
       a-=1
       c+=1
       ans.append('C')
     else:
       a+=1
       c-=1
       ans.append('A')
 elif arr[i]=='BC':
   if b==1 and c==1: #どちらも1のときは次の操作によりどちらの要素を増やすかを決定する
     if i==n-1 or arr[i+1]=='BC' or arr[i+1]=='AB':
       b+=1
       c-=1
       ans.append('B')
     elif arr[i+1]=='AC':
       b-=1
       c+=1
       ans.append('C')
   elif b==0 and c==0: #どちらも0のときはそこで操作が終了する
     print('No')
     break
   else: #少なくとも一方が0でないときは、大きい方の値を減らし、小さい方の値を増やせばよい
     if b>=c:
       b-=1
       c+=1
       ans.append('C')
     else:
       b+=1
       c-=1
       ans.append('B')
else: #操作を完了できたときは答えを出力する
 print('Yes')
 for i in range(n):
   print(ans[i])

この記事が気に入ったらサポートをしてみませんか?