1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AtCoderBeginnerContest164復習&まとめ(後半)

Posted at

AtCoder ABC164

2020-04-26(日)に行われたAtCoderBeginnerContest164の問題をA問題から順に考察も踏まえてまとめたものとなります.
後半ではDEFの問題を扱います(時間の関係上Dしかまともにまとめられてないです).前半はこちら
問題は引用して記載していますが,詳しくはコンテストページの方で確認してください.
コンテストページはこちら
公式解説PDF

D問題 Multiple of 2019

問題文
"1"から"9"までの数字のみからなる文字列$S$が与えられます。
次のような条件を満たす整数の組$(i,j)$ $(1 \leq i \leq j \leq |S|)$の総数を求めてください。
条件:$S$の$i$文字目から$j$文字目までを$10$進法の整数としてみると、この整数は$2019$の倍数である。

最初,制約が$(1 \leq S \leq 200000)$だと思って,愚直にやってTLEでした.
Cまでの問題がいつも以上のハイペースで解けていたので,都合よく解釈してましたね.
TLE出て,問題読み直して絶望しました.

$(1 \leq |S| \leq 200000)$

そりゃ間に合わないわ…
その後,E問題とF問題見に行って,残り90分をこの問題に使うことを決心しましたが,解けませんでした.
終わったあとTwitter上を見ると,どうやらABC158 E問題の類題とのこと.
やっぱり,しっかりと解けなかった問題を解き直ししないことには,成長は見込めないなと痛感しました(凡人はひたすら,過去問をやり,類似問題を解けるようにするしかないないですよね汗).
張り切って解説読んだり,過去の問題見て記事にまとめようと思ったのですが,4/28に @Seika139 さんが,「数弱でも分かる ABC164 D問題 (剰余演算)」というタイトルの記事を投稿していて,そっと自分が書いていたものを消しました.
こんなにまとまっている記事あったら,もう書けないです(汗)
そして,この記事も参考にさせてもらい,学ばせていただきました.
とりあえず,コードも「数弱でも分かる ABC164 D問題 (剰余演算)」に掲載されているものを参考に提出しました.

abc164d.py
S = input()[::-1]
ans = 0
mods = [0] * 2019
mods[0] = 1
current = 0
x = 1
for s in S:
    current = (current + x * int(s)) % 2019
    ans += mods[current % 2019]
    mods[current % 2019] += 1
    x = x * 10 % 2019
print(ans)

個人的に,ans += mods[current % 2019] でコンビネーションの計算ができることをそもそも知らなかったので,勉強になりました.
基本的には,参考にした記事に書いてある$D[j] = D[i]$となる組み合わせの数を数える必要性があるのですが,自分は$n$が決まってから計算する方法しか知りませんでした.

例えば,今まで$D[i]=5$となる$i$が$m$個見つかっていて,新しく$D[j]=5$となる$j(i\not=j)$が見つかったとき,

{}_{m+1} C _2 = {}_m C _2 + m

であることから,$m$を足してくことで計算できるとは...
恥ずかしながら知りませんでした.
これで,計算をし終えた後に${}_n C _2$を計算する必要がないんですね.

参考にした記事に書いてないような気がしたので少し,書けたらと思うのですが,mods[0] = 1してる理由についてのことです.
これは$D[i]=0$のときについての話です.
参考にした記事で挙げている例では,$D[i]=0$が出ていないので,二つほど例を挙げてD問題を終わりたいと思います.

入力例1 "18171"

入力例1は$S=18171$のときです.このとき$D[i]$は$i=0$から順に,
$1, 71, 171, 95, 0$
となります.
ここで,$D[j] = D[i]$であるものが存在しないため,出力を"0"にしてしまいそうですが,"18171"は$2019$の倍数です.
次に入力例2を見ていきます.

入力例2 "1817118171"

入力例2は$S=1817118171$のときです.このとき$D[i]$は$i=0$から順に,
$1, 71, 171, 95, 0, 1069, 1196, 1089, 605, 0$
となります.
$D[4] = D[9]$であるため,出力を"1"にしてしまいそうですが,"18171"は$2019$の倍数であり,"1817118171"も$2019$の倍数であることから,正しい出力は"3"となります.

結論

$D[i]=0$のみは特殊で,2か所の$D[i]=0$と$D[j]=0$を選ばなくても,$D[i]=0$のみで割り切ることができるため,数が異なります.
参考にした記事では,

実際の配列$D$は長さが$N$ではなく$N+1$として、$D[N+1]=0$という空き部屋を一つ用意します。これは文字列$S$をスライスするインデックス$i,j$の選び方が${}_{N+1}C_2$通りあることと関係があります。

と書いてあったので,ここに対応すると思うのですが,$D[N+1]=0$というない部分を追加することで,$D[N+1]=0$と$D[i]=0$の組み合わせを選ぶことが,$D[i]=0$のみを選ぶことに対応するようになります.
これにより計算ができるようになるため,最初に$D[N+1]=0$があるということにするために,mods[0] = 1としています.

実際のコンテストで,ここまで実装に頭が回るようになる気がしない…

E問題 Two Currencies

問題文
$1$から$N$までの番号がつけられた$N$個の都市があります。 これらの都市は$M$本の鉄道路線によって結ばれています。
あなたは現在、金貨を$10^{100}$枚、銀貨を$S$枚持った状態で都市$1$にいます。
$i$番目の鉄道路線は、都市$U_i$と都市$V_i$を双方向に結んでおり、片道の運賃は銀貨$A_i$枚、移動にかかる時間は$B_i$分です。 運賃を金貨で払うことはできません。
各都市には両替所があり、都市$i$の両替所では金貨$1$枚を銀貨$C_i$枚と交換することができます。 交換には、金貨$1$枚あたり$D_i$分かかります。
各交換所では、金貨を何枚でも交換することができます。
$t=2,...,N$について、都市$1$から都市$t$への移動にかかる最小の時間を求めてください。電車を待つのにかかる時間は無視して構いません。

dijkstra法を適用する問題のようでしたが,そもそも単純なdijkstra法の実装ですら慣れていないので,まずはそのあたりを1から勉強しないとなと思います.
提出されたコードの中で最初にpythonでAC通していたKiri8128さんのコードを参考にさせてもらいました.@kiri8128

abc164e.py
from heapq import heapify, heappush as hpush, heappop as hpop
N, M, S = map(int, input().split())
k = 2451
X = [[] for _ in range(N * k)]
for _ in range(M):
    u, v, a, b = map(int, input().split())
    u, v = u-1, v-1
    for i in range(a, k):
        X[u * k + i].append([v * k + i - a, b])
        X[v * k + i].append([u * k + i - a, b])

for i in range(N):
    c, d = map(int, input().split())
    for j in range(k - 1):
        X[i * k + j].append([i * k + min(j + c, k - 1), d])

def dijkstra(n, E, i0=0):
    h = [[0, i0]]
    D = [-1] * n
    done = [0] * n
    D[i0] = 0
    
    while h:
        d, i = hpop(h)
        done[i] = 1
        for j, w in E[i]:
            nd = d + w
            if D[j] < 0 or D[j] > nd:
                if done[j] == 0:
                    hpush(h, [nd, j])
                    D[j] = nd
    return D

D = dijkstra(N * k, X, min(S, k - 1))
print(*[min(D[i * k:(i+1) * k]) for i in range(N)][1:], sep = "\n")

まだまだ勉強不足なので,このあたりはゆっくり勉強していけたらと思ってます.

F問題 I hate Matrix Construction

問題文
整数$N$及び長さ$N$の配列$S,T,U,V$が与えられます。以下の条件を満たすような$N×N$の行列$a$をどれか$1$つ構築してください。
 ・$a_{i,j}$は整数である。
 ・$0 \leq a_{i,j} \leq 2^{64}$
 ・$S_i = 0$のとき$i$行目の要素のビットごとの論理積は$U_i$である。
 ・$S_i = 1$のとき$i$行目の要素のビットごとの論理積は$U_i$である。
 ・$S_i = 0$のとき$i$列目の要素のビットごとの論理積は$V_i$である。
 ・$S_i = 1$のとき$i$列目の要素のビットごとの論理積は$V_i$である。
ただし、条件を満たす行列が存在しない場合もあるかもしれません。

現状,DもEも解けていないので,いつかやり直します(汗)
まずは解けそうなところから地道にやっていくしか.

流石にEやFを毎回きちんと整理することはできなさそうです.
時間があるときにやり直せたらと思います.
後半も最後まで読んでいただきありがとうございました.

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?