投稿日:
- この記事は OUCC Advent Calendar 2025 の14日目の記事です。
- ABC436に参加した感想を書きます。(
ネタがない(2回目))
AtCoderとは
- AtCoderはざっくりいうとプログラミングの問題を解くコンテストを開催しているサイトです。ABCはAtCoder Beginner Contestの略で、初心者向け?のコンテストです。大体毎週土曜日の21:00から開催され、制限時間は100分です。
- リンク: https://atcoder.jp/ ## 全体の感想
- 今日はE問題まで解けたのでそこまで解説しますが、Eが完全エスパーなので解説とは?になります。
A問題
- 問題文: https://atcoder.jp/contests/abc436/tasks/abc436_a
- 解法: oを先頭にN-len(S)個追加してからSを追加すれば構築可能です。
- 提出コード
N=int(input())
S=input()
ans=[]
for i in range(N-len(S)):
ans.append("o")
for i in range(len(S)):
ans.append(S[i])
print("".join(ans))
B問題
- 問題文: https://atcoder.jp/contests/abc436/tasks/abc436_b
- 解法: 本当に問題文通りに実装をします。頭がバグらないようにni,njは入れるもの、i,jは以前の位置を保持するようにしました。
- 提出コード
N=int(input())
G=[[0]*N for _ in range(N)]
f=0
i=0
j=(N-1)//2
G[i][j]=f+1
f+=1
while f<N*N:
ni=(i-1)%N
nj=(j+1)%N
if G[ni][nj]!=0:
ni=(i+1)%N
nj=j
G[ni][nj]=f+1
f+=1
i=ni
j=nj
for ans in G:
print(*ans)
C問題
- 問題文: https://atcoder.jp/contests/abc436/tasks/abc436_c
- 解法: 2×2の正方形が重なるいうことは4つのマスのいずれかが既に使われていると考えればsetを使えばできそうです。
- 提出コード
N,M=map(int,input().split())
S=set()
ans=0
L=[(0,0),(0,1),(1,0),(1,1)]
for i in range(M):
a,b=map(int,input().split())
f=True
for di,dj in L:
na=di+a
nb=dj+b
if (na,nb) in S:
f=False
if f:
for di,dj in L:
na=di+a
nb=dj+b
S.add((na,nb))
ans+=1
print(ans)
D問題
- 問題文: https://atcoder.jp/contests/abc436/tasks/abc436_d
- 解法: 前回でも使いましたBFSです。ワープの場合がだるかったんですが、生ける先を管理してa,bからの移動先として実装しました。一度ワープを使ったらもう一度そこに行く場合は必ず手数が増えるので考えないようにします。ワープ用のdist更新を行わない場所(超頂点)を作ってもよかったかも。
- 提出コード
L=[(1,0),(-1,0),(0,1),(0,-1)]
H,W=map(int,input().split())
E=[[] for i in range(26)]
G=[list(input()) for _ in range(H)]
dist=[[-1]*W for _ in range(H)]
visited=[False]*26
dist[0][0]=0
for i in range(H):
for j in range(W):
if G[i][j]!="." and G[i][j]!="#":
E[ord(G[i][j])-ord("a")].append((i,j))
from collections import deque
q=deque()
q.append((0,0))
while q:
a,b=q.popleft()
for di,dj in L:
na=a+di
nb=b+dj
if 0<=na<H and 0<=nb<W:
if dist[na][nb]!=-1:
continue
if G[na][nb]=="#":
continue
else:
q.append((na,nb))
dist[na][nb]=dist[a][b]+1
if G[a][b]!="." and G[a][b]!="#":
if visited[ord(G[a][b])-ord("a")]==True:
continue
else:
visited[ord(G[a][b])-ord("a")]=True
for na,nb in E[ord(G[a][b])-ord("a")]:
if dist[na][nb]!=-1:
continue
q.append((na,nb))
dist[na][nb]=dist[a][b]+1
print(dist[-1][-1])
E問題
- 問題文: https://atcoder.jp/contests/abc436/tasks/abc436_e
- 解法: 入れ替えをすることでソートが行われるものを考えます。例えば2 1 4 3という並びがあると1と3,4と入れ替えても意味はありません。よってこれは(2,1)と(4,3)のグループに分けて考えることができます。このグループ分けはUnion-Findで実装可能です。各個数においては、よくわかりませんが実験の結果1個なら0通り、2個なら1通り、3個なら3通り、4個なら6通りだったので、n*(n-1)/2通りになると予想しました。(通ったので正しいはず)
- 提出コード
N=int(input())
A=list(map(int,input().split()))
class UnionFind():
# 初期化
def __init__(self, n):
self.par = [-1] * n
self.rank = [0] * n
self.siz = [1] * n
# 根を求める
def root(self, x):
if self.par[x] == -1: return x # x が根の場合は x を返す
else:
self.par[x] = self.root(self.par[x]) # 経路圧縮
return self.par[x]
# x と y が同じグループに属するか (根が一致するか)
def issame(self, x, y):
return self.root(x) == self.root(y)
# x を含むグループと y を含むグループを併合する
def unite(self, x, y):
# x 側と y 側の根を取得する
rx = self.root(x)
ry = self.root(y)
if rx == ry: return False # すでに同じグループのときは何もしない
# union by rank
if self.rank[rx] < self.rank[ry]: # ry 側の rank が小さくなるようにする
rx, ry = ry, rx
self.par[ry] = rx # ry を rx の子とする
if self.rank[rx] == self.rank[ry]: # rx 側の rank を調整する
self.rank[rx] += 1
self.siz[rx] += self.siz[ry] # rx 側の siz を調整する
return True
# x を含む根付き木のサイズを求める
def size(self, x):
return self.siz[self.root(x)]
uf=UnionFind(N+1)
for i in range(N):
uf.unite(i+1,A[i])
ans=0
S=set()
for i in range(N):
a=uf.root(i+1)
if a not in S:
b=uf.size(a)
ans+=b*(b-1)//2
S.add(a)
print(ans)
こっからは解けなかったんですが、問題文だけ乗せときます。
F問題
G問題
最後に
以上でabc436の感想を終わります。ここ2回分の感想を書いてるのですが重要なアルゴリズムやデータ構造がたくさん出てきているので書いていて楽しいです。あとは次で2分探索、累積和、動的計画法のどれかが出てくれると嬉しい。さてこれで感想記事を終わります。AtCoderに興味を持ったら一度参加してみましょう!!!来週もおそらく感想記事を書くので、そちらもよろしくお願いします。