あるぱかのブログ

AtCoder茶(レート578)

モノグサプログラミングコンテスト2024(AtCoder Beginner Contest 345)

A - Leftrightarrow

リンク:A - Leftrightarrow (atcoder.jp)

概要:<、=、>からなる文字列 Sが、<==…==>のようになっているか判定してね

文字列 Sの1文字目が"<"、末尾が">"、その他が全て"="であればYes、そうでなければNoを出力します。

そのまま S[0]="<"、 S[-1]=">"、 S[1:-2]= "="かどうか確認します。

#ABC345 A - Leftrightarrow
S = input()
#正誤判定のフラグ 条件を満たさないと分かったときにFalseに変える
flag = True
if S[0] != '<':
    flag = False
if S[-1] != '>':
  flag = False
for i in range(1, len(S)-1):
    if S[i] != '=':
        flag = False
#すべての判定を潜り抜けたらYesを出力、どこかで引っかかったらNoを出力
if flag == True:
    print('Yes')
else:
  print('No')

提出リンク:提出 #51278901 - モノグサプログラミングコンテスト2024(AtCoder Beginner Contest 345)

B - Integer Division Returns

リンク:B - Integer Division Returns (atcoder.jp)

概要:整数 Xに対して  \dfrac {X}{10}以上で最小の整数を出力してね

 Xが10の倍数ならそのまま \dfrac{X}{10}を、そうでなければ \dfrac{X}{10}+1を出力すれば題意を満足します。

AtCoderでは上界 \lceil X \rceilと下界 \lfloor X \rfloorは頻繁に出てくるので覚えておきましょう。

それぞれ天井関数、床関数とも言います。

今回のA問題よりこっちのほうが簡単じゃない・・・?

#ABC345 B - Integer Division Returns
X = int(input())
#余りが0なら10の倍数なのでそのまま、違う場合は+1して出力
if X % 10 == 0:
    print(X//10)
else:
  print(X//10 +1)

提出リンク:提出 #51282581 - モノグサプログラミングコンテスト2024(AtCoder Beginner Contest 345)

C - One Time Swap

リンク:C - One Time Swap (atcoder.jp)

概要:英小文字からなる文字列 Sの2か所を選んで入れ替えを行うときに最大何通りの文字列ができるか出力してね

文字列の長さN = 10^{6}なので組み合わせ数は最大 _{10^{6}} C _2=5 \times 10^{10}なので愚直に全パターン試すと当然TLEします。

全パターンから重複するものを引けば答えが出そうなのでその方針で行きます。

いつもの度数分布で各文字が何個あるかチェックを行い、度数分布 numの要素数 n = 文字列 Sの長さ Nであれば答えは _N C _2となります。

重複がある場合、仮にabcccのようにcが3文字あれば重複する数は _3 C _2なので上記の組み合わせ数から引けばよさそうです。

一般化すれば特定の文字が D回あるとすると重複する数は _D C _2です。

ただ今回は文字列入れ替え前をカウントしていないので、入出力例2のような場合に _5 C _2 - _5 C _2 = 0となり引きすぎてしまうので、重複がある場合のみ+1して初期の文字列分を考慮します。

中学か高校数学でこんな感じの問題あったような気がする・・・

#ABC345 C - One Time Swap
from collections import defaultdict
S = input()
N = len(S)
#度数分布作成
num = defaultdict(int)
for key in S:
    num[key] += 1
#print(num)
#すべて違う文字としたときの総組み合わせ数
ans = N*(N-1) // 2
#度数分布の要素数=文字列の種類
n = len(num)
#すべてバラバラの文字ならansをそのまま出力
if N == n:
    print(ans)
#違う場合はansから重複分を引いて+1した数を出力
else:
    for key in num:
        if num[key] > 1:
            ans -= num[key] * (num[key] - 1) // 2
    ans += 1
  print(ans)

提出リンク:提出 #51309245 - モノグサプログラミングコンテスト2024(AtCoder Beginner Contest 345)

ここまで28分、最終順位2735 / 10047位で大変満足です。

D - Tiling

リンク:D - Tiling (atcoder.jp)

概要:手持ちのタイルを使ったり使わなかったりしてマス目をぴったり埋められるか判断してね

解けなかったのでお気持ちだけ。

タイルのサイズを受け取ると同時に各タイルの面積をメモしておき、まずはDPで面積ぴったりになるタイルの組み合わせを探す。面積が合わないならNoを出力して終了。

上記で選んだタイルが占有する座標をそれぞれ列挙する。

全探索で座標の重複なく置けるかどうか確かめる。

置けなければ90°回転(座標(x, y) →(y, -x))でまた全探索。

マス目ぴったりになればYesを出力。

1番目のDPするところまでは書けましたが残りがね・・・diffどのくらいだろう、緑上位か水色?

結果

いつも通りA~Cの3完28minでした。

今はまだ実装できないけども解法がぼんやり浮かぶくらいにはなったような気がします。

初めての緑パフォ!

色変(灰→茶)

ABC344にてレート351→404となり茶色になりました。

色変までのABC参加回数は14回でした。



・やったこと

鹿本(~レート300くらい)

AtCoder ProblemsのRecommendationを解く(レート250~350くらい)

鉄則本(レート350~)

・できること

計算量の見積もり

全探索

二分探索

累積和

BFS、DFS

DP(初歩)

 

これまではA~C問題の早解きでレート上げてきたので、今後はD問題を解けるようにならないと茶色中位~緑には上がれないと感じています。

2024年12月終わるまでに緑になりたい。

トヨタ自動車プログラミングコンテスト2024#3(AtCoder Beginner Contest 344)

A - Spoiler

リンク:A - Spoiler (atcoder.jp)

概要:文字列 Sに2個ある"|"の中身を削除した文字列を出力してね

 Sの先頭から調べていって"|"の位置を記憶します。

後はスライス記法で必要な文字列を出力します。

#ABC344 A - Spoiler
S = input()
pos = []
#|の位置を調べて配列posにメモ
for i in range(len(S)):
    if S[i] == "|":
       pos.append(i)
#print(pos)
print(S[:pos[0]]+S[pos[1]+1:])  

提出リンク:提出 #51026649 - トヨタ自動車プログラミングコンテスト2024#3(AtCoder Beginner Contest 344)

B - Delimiter

リンク:B - Delimiter (atcoder.jp)

概要:個数がわからない配列 Aを順番に投げるので受け取った配列を逆順で出力してね。

 A_Nは0で他は0以外なので0を受け取るまで入力を配列 Aに追加します。

最後に逆順で出力します。

#ABC344 B - Delimiter
A = []
#無限ループで入力受け取り 入力=0で打ち切り
while True:
    a = int(input())
    A.append(a)
    if a == 0:
        break
B = A[::-1]
for i in B:
    print(i)  

提出リンク:提出 #51033581 - トヨタ自動車プログラミングコンテスト2024#3(AtCoder Beginner Contest 344)

C - A+B+C

リンク:C - A+B+C (atcoder.jp)

概要:3つの配列 A, B, Cからひとつずつ取り出して和をとったときに X_iと一致できるか判定してね。

配列 A, B, Cの長さ N, M, Lは最大で100なので全組み合わせを探索しても最悪 10^{6}通りです。

先に A+B+Cを全探索して和をsetに入れておくことで、 X_iが実現可能かを O(1)で判定できます。

(setに対してin演算子の計算量が O(1)のため。listに対しては要素数 Nとすると O(N)になるためTLEします)

計算量は A+B+Cの全探索とクエリの処理で O(NML+Q)で実行時間に間に合います。

#ABC344 C - A+B+C
N = int(input())
A = list(map(int, input().split()))
M = int(input())
B = list(map(int, input().split()))
L= int(input())
C = list(map(int, input().split()))
S = set()
#A+B+Cの計算結果をsetに追加
for i in range(N):
    for j in range(M):
        for k in range(L):
            S.add(A[i]+B[j]+C[k])
#print(S)
Q = int(input())
X = list(map(int, input().split()))
#setの中にXiがあるが判定&出力
for i in range(Q):
    if X[i] in S:
        print('Yes')
    else:
        print('No')  

提出リンク:提出 #51040590 - トヨタ自動車プログラミングコンテスト2024#3(AtCoder Beginner Contest 344)

D - String Bags

リンク:D - String Bags (atcoder.jp)

概要: i個の袋から文字列を取り出して組み合わせて文字列[tex :T]ができるか、できる場合は最小の操作回数を出力してね

近鉄則本を買って少しだけDP勉強したのでDPで解くんだろうなということはわかりました。

ただ問題文をよく読んでいなくて袋は1, 2, …,  i, …,  Nの順番に見ていくこと、取り出した文字列を末尾に付け足すことの2点を見落としていて迷走した結果解けず。

二次元DPで i= i番目までの袋を見る、 j=文字列の完成度(長さ)、 dp_{ij}=コストを管理すればいいのかな?後日解き直したい。

結果

C問題までの3完、時間は11:24で4662位でした。

ついに色変!

 

 

 

 

AtCoder Beginner Contest 343

A - Wrong Answer

リンク:A - Wrong Answer (atcoder.jp)

概要:0~9の整数のうち A+B以外をどれでもいいので1つ出力してね

そのまんまなので0~9を順番に見ていって A+Bではなかった時点で出力して終了です。

#ABC343 A - Wrong Answer
A, B = map(int, input().split())
for i in range(10):
    if i != A+B:
        print(i)
        break  

提出リンク:提出 #50769083 - AtCoder Beginner Contest 343

B - Adjacency Matrix

リンク:B - Adjacency Matrix (atcoder.jp)

概要: N個の頂点があり配列 Aの要素 A_{ij}=1の時頂点 i jは接続されています。頂点 iとつながっている頂点の番号を出力してね

問題文で無向グラフとか頂点とか辺とか言われているので何も考えずグラフ作って順番に出力しました。

#ABC343 B - Adjacency Matrix
N = int(input())
G =[[] for i in range(N)]
for i in range(N):
    A = list(map(int, input().split()))
    for j in range(N):
#A[i][j]が1の場合頂点iの接続先に頂点jを追加する
        if A[j] == 1:
#1-indexedになるようにjに+1
            G[i].append(j+1)
for i in range(N):
#グラフの各頂点からつながる頂点の番号を半角スペース区切りで出力
  print(*G[i], sep = ' ')

提出リンク:提出 #50784718 - AtCoder Beginner Contest 343

C - 343

リンク:C - 343 (atcoder.jp)

概要: N以下の立方数( x^{3})で回文になっている最大の数 Kを出力してね

 N \leq 10^{18}の制約です。

 Kを0から Nまで全探索すると当然TLEになるので xについて全探索します。

 N=10^{18}でも x=10^{6}なので十分間に合います。

回文かどうかの判定は x^{3}を文字列として扱うと簡単です。

 s = str(x^{3})として、 s[j] = s[-j-1] sの桁数半分(切り上げ)まで見てやれば判断できます。

探索する xの数は 10^{6}以内、回文の判定試行回数は Nの桁数半分の高々9回程度なので十分間に合います。

#ABC343 C - 343
N = int(input())
#探索範囲はNの3乗根切り上げから0まで
for i in range(int(N**(1/3))+2, 0, -1):
    if i**3 <= N:
#iの3乗を文字列sとして扱う
        s = str(i**3)
        n = len(s)
#回文判定のフラグ
        flag = True
#sの桁数の半分切り上げまで探索する
        for j in range((n+1)//2):
            if s[j] != s[-j-1]:
#回文ではない場合にNG判定
                flag = False
                break
#NG判定なくループを越えたらiの3乗=Kを出力して終了
        if flag == True:
            print(i**3)
            break
 

提出リンク:提出 #50811831 - AtCoder Beginner Contest 343

 

D - Diversity of Scores

リンク:D - Diversity of Scores (atcoder.jp)

概要: N人が何らかの競技をしていて T秒時に A_iさんが B_i点獲得しました。この時点で点数が何種類になっているか出力してね

 N \leq 2 \times 10^{5} T \leq 2 \times 10^{5}なので O(NT)だとTLEになります。

制約を気にせず簡単に書くと次のようになります(当然TLEします)。

#ABC343 D - Diversity of Scores
N, T = map(int, input().split())
ans = [0] * N
for i in range(T):
    a, b = map(int, input().split())
    ans[a-1] += b
    print(len(set(ans)))  

提出リンク:提出 #50817856 - AtCoder Beginner Contest 343

原因は配列 ansのsetをとるときに O(N)かかるためです。

なのでお気持ちとしてはlistのsetを毎回とるのではなくて最初からsetやdictを使いたい。

 B_iをsetに加えていくだけでは入力例1のように10点を2回とって20点になる場合と20点を1回とる場合を分離できない。

dictでその点数になっている人数を管理するにも誰が何点かを管理するいい方法が思いつかない。

本番中1時間くらい考えて思いつきませんでした。

順位表みたらA問題提出した人数の半分くらい解けてるので茶diff下位か高くても中位くらいかな?是非ともACしたかった・・・

解説見ると得点を管理するlistと度数分布を管理するdictの合わせ技みたいですね。dictの追加と削除は使ったことがなかったので大変勉強になりました。

Counterで都度度数分布を作ると毎回 O(N)ですがdictの追加と削除はどちらも O(1)なので間に合うというわけですね。

解説ACしたものが以下になります。

#ABC343 D - Diversity of Scores
#解説
from collections import defaultdict
N, T = map(int, input().split())
now = [0] * N
#dictを0で初期化
ans = defaultdict(int)
#keyを点数、ans[key]に人数を格納していく
#初期条件は0点がN人
ans[0] = N
for i in range(T):
    a, b = map(int, input().split())
#今からaさん(0-indexed)の点数が変動するので度数分布から-1する
    ans[now[a-1]] -= 1
#もし0人になったらkeyを削除
    if ans[now[a-1]] == 0:
        del ans[now[a-1]]
#点数を管理するリストでaさんの点数にb点を足す
    now[a-1] += b
#点数変動したので度数分布更新
  ans[now[a-1]] += 1
    print(len(ans))  

提出リンク:提出 #50845459 - AtCoder Beginner Contest 343

結果

3完36min+1WAでした。

(C問題で探索範囲をミスった)

今回のD問題はACしておきたかった・・・

 

 

 

 

 

 

 

 

精進(2024/2/25)

AtCoder ProblemsのRecommendation(Moderate)を解く

AtCoder Beginner Contest 157 C - Guess The Number

diff = 456

リンク:C - Guess The Number (atcoder.jp)

概要: N \leq 3桁の数字の各桁について M個の指示が矛盾しない最小の数字を出力してね

最大3桁、条件数最大5回なので0~999の順に全探索してもいいような気がしますがうまい方法が思いつきませんでした。

なので各桁を-1で初期化した配列 ansを用意して、指示の通り数字を置き換える方針としました。

最終的に-1を0もしくは1に置き換えて出力しています。

この問題で-1を出力すべきは以下の2通りです。

条件1:同じ桁に違う数字を入れようとしたとき

条件2:2桁以上の数字で一番大きい位が0のとき

条件1は配列操作の段階で排除しています。

 flag=Falseとして識別しています)

条件2は N=1のときのみ0が許容されるので面倒です。

なので配列が完成した後に N=1かどうか、違う場合は一番左の桁に0が入っていないか判定を入れました。

N, M = map(int, input().split())
ans = [-1] * N
flag = True
for _ in range(M): #桁の指定条件受け取りと条件1の判定
    s, c = map(int, input().split())
  if ans[s-1] == c: #すでに書き換え済の桁に同じ数字を受け取った場合は処理を行わず流す
        continue
  if ans[s-1] == -1: #書き換え前の場合はcに書き換える
        ans[s-1] = c
  else: #書き換え済かつ違う数字を受け取ったときはNG判定
        flag = False
        break
if flag == True: #条件1をクリアした場合
  if N == 1: #1桁で数字指定が無い場合は0を出力
        if ans[0] == -1:
            print(0)
        else:
            print(ans[0])
  else: #2桁以上の時
      if ans[0] == 0: #1番大きい桁が0のときNG判定
            print(-1)
        else:
            for i in range(N):
              if i == 0 and ans[i] == -1: #最大の桁が書き換えされていなければ1にする
                    ans[i] = 1
                else:
                  if ans[i] == -1: #他の桁は0にする
                        ans[i] = 0
            print(*ans, sep = '')
else:
  print(-1) #条件1を満足しなかった場合は-1を出力  

提出リンク:提出 #50639848 - AtCoder Beginner Contest 157

NOMURA プログラミングコンテスト 2021 (AtCoder Regular Contest 121) A - 2nd Greatest Distance

diff = 459

リンク:A - 2nd Greatest Distance (atcoder.jp)

概要: N個ある家同士の距離を定義に沿って計算して2番目に遠い距離の値を出力してね

 N \leq 2 \times 10^{5}、問題文にある通り組み合わせは \dfrac{N(N-1)}{2}通りなので愚直に全探索した場合 O(N^{2})でTLEになります。

何とかして計算量を O(N) O(NlogN)などに落とせないか考えます。

問題文から座標 (x_i, y_i)の絶対値が大きいものだけ見ればいいことがわかるので探索する対象を絞ることができ、ソートによる計算量 O(NlogN)まで落とせそうです。

具体的には x座標の大きいもの2軒+小さいもの2軒、 y座標の大きいもの2軒+小さいもの2軒で合計8軒程度を見れば答えが出せます。

家の座標リストを x座標と y座標でソートしたいので、 x座標でソートしやすいように (x, y)の順で記録していく配列 xdと、 y座標でソートしやすいように (y, x)の順で記録していく配列 ydの2つを作りました。

別々のリストで管理するため同じ座標を格納した際に同じ家なのか違う家なのか判断できるように、 (x, y, i)のように座標と家の番号(座標を受け取った順)の3つの情報を保持するようにしました。

あとはそれぞれのリストで距離を計算して配列 distにメモするのですが、 x座標から取り出した配列 xdd y座標から取り出した配列 yddとで同じ家同士の距離を計算しては不正解になりますので重複しないように配列 seenで管理しました。

計算量はソート部分に最も時間がかかるので O(NlogN)で十分高速です。

4軒の家同士の組み合わせ数は高々6通り、その家が探索済みかを O(N)かかるin演算子で探してますがここでの Nは高々4なので探索対象の家を正しく取り出した後は少々ガバってもTLEにはならないと思います。

N = int(input())
xd = []
yd = []
for i in range(N):
    x, y = map(int, input().split())
    xd.append((x, y, i))
    yd.append((y, x, i))
xd = sorted(xd)
yd = sorted(yd)
xdd = sorted(xd[:2] + xd[-2:]) #x座標でソートして上から2つ、下から2つを取り出し
ydd = sorted(yd[:2] + yd[-2:]) #y座標でソートして上から2つ、下から2つを取り出し
#print(xdd, ydd)

seen = []
dist = []
for i in range(len(xdd)):
    for j in range(i, len(xdd)):
      if xdd[i] == xdd[j]: #N=3のとき家の重複が出るので計算から除外
            continue
        else:
            d = max(abs(xdd[i][0] - xdd[j][0]), abs(xdd[i][1] - xdd[j][1]))
            dist.append(d)
          seen.append((xdd[i][1], xdd[i][0], xdd[i][2])) #yddの計算で同じ家同士を計算しないように参照した家をメモ
            seen.append((xdd[j][1], xdd[j][0], xdd[j][2]))
for i in range(len(ydd)):
    for j in range(i, len(ydd)):
        if ydd[i] == ydd[j]:
            continue
      if ydd[i] in seen and ydd[j] in seen: #xddで見た家同士の計算を除外
            continue
        else:
            d = max(abs(ydd[i][0] - ydd[j][0]), abs(ydd[i][1] - ydd[j][1]))
            dist.append(d)
dist = sorted(dist, reverse=True)
print(dist[1])   #距離大きい順に2番目を出力

提出リンク:提出 #50640693 - NOMURA プログラミングコンテスト 2021 (AtCoder Regular Contest 121)

 

HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

A - Yay!

リンク:A - Yay! (atcoder.jp)

概要:文字列 Sの中で唯一違う文字が先頭から何番目か出力してね

開始前に配点表みてA問題なのに配点150点でビビり散らかしてました。

文字列 Sの長さが100なので制約は厳しくなく、1文字しかない文字が何かさえ分かれば文字列のindex()演算子で答えが出せます。

私は文字をkeyとした度数分布numを作成して、num[key] = 1であることから問題の文字を特定しました。

出力時に1-indexedに戻すことを忘れずに。

#ABC342 A - Yay!
from collections import Counter
S = input()
num = Counter(S)
#print(num)
for key in num:
    if num[key] == 1:
        print(S.index(key)+1)  

これを入力例3に対して実行すると以下の出力が得られます(コメントアウトしている部分も有効化しています)

zzzzzwz                      #入力
Counter({'z': 6, 'w': 1}) #度数分布
6   #1-indexedしたS.index(w)の結果出力

提出リンク:提出 #50560688 - HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

B - Which is ahead?

リンク:B - Which is ahead? (atcoder.jp)

概要: N人が配列 Pの順番で並んでいます。 A_iさんと B_iさんを比べて前にいる人を Q回出力してね

 N \leq 100 Q \leq 100なので毎回index(A)とindex(B)の大小比較しても通りそうな気がしましたが、念のため計算量 O(N)かかるindexではなく O(1)で出せるように工夫しました。

#ABC342 B - Which is ahead?
from collections import defaultdict
N = int(input())
P = list(map(int, input().split()))
num = defaultdict(int)
for i in range(N):
    num[P[i]] = i
#print(num)

Q = int(input())
for _ in range(Q):
    A, B = map(int, input().split())
    a = num[A]
    b = num[B]
    if a < b:
        print(A
        )
    else:
      print(B)  

これを入力例1に対して実行すると以下の出力が得られます(コメントアウトしている部分も有効化しています)

 3                                               #入力N
2 1 3 #入力P
defaultdict(<class 'int'>, {2: 0, 1: 1, 3: 2}) #人をkeyとして並び順を保有するdict
3 #入力Q
2 3 #クエリ1
2 #Aのほうが前なので2を出力
2 3 #クエリ2
2 #Aのほうが前なので2を出力
1 3 #クエリ3
1   #Aのほうが前なので1を出力

提出リンク:提出 #50572209 - HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

69msec

ちなみにdictを使わず普通に書いたコードは以下です。

72msecでACできています。

#ABC342 B - Which is ahead?
N = int(input())
P = list(map(int, input().split()))
Q = int(input())
for _ in range(Q):
    A, B = map(int, input().split())
    if P.index(A) < P.index(B):
        print(A)
    else:
        print(B)  

提出リンク:提出 #50613654 - HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

C - Many Replacement

リンク:C - Many Replacement (atcoder.jp)

概要:文字列 Sの中で c_iに該当する文字を d_iに書き換える操作を Q回行った最終的な文字列を出力してね

 N \leq 2 \times 10^{5} Q \leq 2 \times 10^{5}なので計算量に気を付けないといけません。

またpythonでは文字列の一部置換が出来ないので別で答え出力用の配列を作成する必要があります。

 Sを都度操作していると間に合わなそうに感じたので、アルファベット小文字のa~zを並べた文字列をさらにunicodeに変換した配列 abを準備し、これについて Q回の書き換え操作をすることにしました。

unicodeへの変換はord()演算子で、a=97、b=98、・・・という風に順番に数字が割り振られています。

数字から文字に戻すときはchr()演算子です。

書き換え箇所の参照にindex()演算子を使用していますが高々26回の計算なのでクエリの処理に O(Q)、最後に文字列を出力するのに O(N)なのでトータルの計算量は O(N+Q)だと思います。

#ABC342 C - Many Replacement
N = int(input())
S = input()
Q = int(input())
ab = [97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122]
for i in range(Q):
    c, d = map(str, input().split())
    for j in range(len(ab)):
        if ab[j] == ord(c):
            ab[j] = ord(d)
#print(ab)
s = [0] * N
for i in range(N):
    s[i] = chr(ab[ord(S[i])-97])
print(*s, sep = '')

これを入力例1に対して実行すると以下の出力が得られます(コメントアウトしている部分も有効化しています)

  7          #入力N
atcoder #入力S
4 #入力Q
r a #クエリ1
t e #クエリ2
d v #クエリ3
a r #クエリ4
[114, 98, 99, 118, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 97, 115, 101, 117, 118, 119, 120, 121, 122] #['r', 'b', 'c', 'v', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'a', 's', 'e', 'u', 'v', 'w', 'x', 'y', 'z']に書き換え
recovea   #t→e,d→vに書き換えた文字列 

提出リンク:提出 #50593350 - HUAWEI Programming Contest 2024(AtCoder Beginner Contest 342)

D - Square Pair

リンク:D - Square Pair (atcoder.jp)

概要:配列 Aの各要素を掛け算した数字が平方数になる組み合わせ数を出力してね

 N \leq 2 \times 10^{5}なので愚直に A_i \times A_jをして都度平方数の判定をしていてはTLEになります(なりました)。

 A_i素因数分解して A_jと足したときに各因数の数がすべて偶数だったらいいのかなぁ等考えていましたが高速に処理できる方法が思いつかず。

公式解説が天才すぎる。競技プログラミングあるところまで行くと数学みたいになる。

 

結果

以上3完でした。

C問題ACが53minで個人的には大満足の結果でした。

レートもようやく338まで上がり茶色が見えてきました。

年度内に色変したい。

(内部レートはABC339くらいから継続して茶色なので茶色コーダー名乗っていいですか?ダメ?はい・・・)

 

精進(2024/2/22)

AtCoder ProblemsのRecommendation(Moderate)を解く

AtCoder Beginner Contest 245 C - Choose Elements

diff = 403

リンク:C - Choose Elements (atcoder.jp)

概要:配列 Aおよび Bから配列 Xを次の条件を守って作成できるか判定してね

条件1: X_i= A_i or  B_i

条件2: |X_i - X_{i+1}| \leq K

 X_iの取り方は A_i or  B_iの2通りなので条件を無視すれば配列 X 2^{N}通り考えられます。

 N \leq 2 \times 10^{5}なので愚直に全組み合わせを探索していては到底間に合いません。

そこで X_iに対して A_{i+1}あるいは B_{i+1}が条件2を満たし得るかのみ考えていくことにします。

(条件を満たした配列1つを出力、あるいは条件を満たす組み合わせ数を出力する問題では無いので、実際に配列 Xを作る必要はありません。)

具体的は A_i B_iを持っておいて(コード9行目の cnt A_{i+1}および B_{i+1}との差の絶対値を計算します。

つまり |A_i - A_{i+1}| |A_i - B_{i+1}| |B_i - A_{i+1}| |B_i - B_{i+1}|の最大4回を計算します。

計算結果が K以下となるような A_{i+1} B_{i+1}を採用して(コード26、28行目の nxt)次のステップの計算を行います。

試行回数は最大で 4 \times Nなので計算量は O(N)となり十分高速です。

#ABC245 C - Choose Elements
N, K = map(int, input().split())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
nxt = [A[0], B[0]]
ans = 'No'

for i in range(N):
    cnt = nxt[:]
    nxt = []
    flag_A = 0
    flag_B = 0
    if i == N-1:
        if len(cnt) != 0:
            ans = 'Yes'
        break
    else:
        if len(cnt) == 0:
            break
        for j in cnt:
            if abs(A[i+1] - j) <= K:
                flag_A += 1
            if abs(B[i+1] - j) <= K:
                flag_B += 1
        if flag_A > 0:
            nxt.append(A[i+1])
        if flag_B > 0:
            nxt.append(B[i+1])
        #print(i, cnt, nxt)
print(ans)  

これを入力例1に対して実行すると以下の出力が得られます(コメントアウトしている部分も有効化しています)

 5 4           #入力N, K
9 8 3 7 2 #入力A
1 6 2 9 5 #入力B
0 [9, 1] [8, 6] #i=0, cnt[A[0], B[0]], nxt[A[1], B[1]]
1 [8, 6] [3, 2] #i=1, cnt[A[1], B[1]], nxt[A[2], B[2]]
2 [3, 2] [7] #i=2, cnt[A[2], B[2]], nxt[A[3]] #B[3]は条件を満たさない
3 [7] [5] #i=3, cnt[A[3]], nxt[B[4]]
Yes #X4となり得る数字を保持してi=N-1に到達したのでYes 

提出リンク:提出 #50516759 - AtCoder Beginner Contest 245

THIRD プログラミングコンテスト 2023 アルゴ(AtCoder Beginner Contest 318) C - Blue Spring

diff = 400

リンク:C - Blue Spring (atcoder.jp)

概要:1単位 D枚綴りの1日周遊パスを買ったり買わなかったりして N日間の旅費を最小にしてね

こちらも N \leq 2 \times 10^{5}なので計算量 O(N^{2})だとTLEになります。

この問題を高速に解くためには累積和が使えそうです。

1日周遊パスの販売単位である D日ごとに累積和をとっておくと後々計算しやすそうです。

前処理として Fの長さが Dの倍数になるように末尾に 0を足しておきます。

後は1日周遊パスを 0 \dfrac{len(F)}{D}単位まで増やしながら旅費の最小値を更新していきます。

1段落目で len(F) Dの倍数になるよう調整、2段落目で D日毎に累積和の計算、3段落目で旅費の計算と最小値の更新をしています。

 Fのソートに最も時間がかかるので計算量は O(Nlog(N))です。

#ABC318 C - Blue Spring
N, D, P = map(int, input().split())
F = list(map(int, input().split()))
F = sorted(F, reverse = True)
if N % D != 0:
    a = D - (N % D)
    F += [0] * a
#print(F)

S = [0]
s = 0
for i in range(len(F) // D):
    s += sum(F[i*D:i*D+D])
    S.append(s)
#print(S)

ans = s
for i in range(len(S)):
    pay = S[-1] - S[i] + P * i
    #print(ans, pay)
    ans = min(ans, pay)
print(ans)  

これを入力例1に対して実行すると以下の出力が得られます(コメントアウトしている部分も有効化しています)

 5 2 10             #入力N, D, P
7 1 6 3 6 #入力F
[7, 6, 6, 3, 1, 0] #Fをソートして末尾に0を足した
[0, 13, 22, 23] #累積和S
23 23 #周遊パス0単位のとき(i=1から始めればよかった・・・)
23 20 #購入数0単位と1単位を比較(ansを20に更新)
20 21 #購入数1単位と2単位を比較
20 30 #購入数1単位と3単位を比較
20   #最小値は周遊パス1単位購入したときの20

提出リンク:提出 #50517168 - THIRD プログラミングコンテスト 2023 アルゴ(AtCoder Beginner Contest 318)