tag:crieit.net,2005:https://crieit.net/tags/%23Atcoder/feed
「#Atcoder」の記事 - Crieit
Crieitでタグ「#Atcoder」に投稿された最近の記事
2020-06-14T04:09:57+09:00
https://crieit.net/tags/%23Atcoder/feed
tag:crieit.net,2005:PublicArticle/15952
2020-06-14T04:09:57+09:00
2020-06-14T04:09:57+09:00
https://crieit.net/posts/2020-5ee5248574c0e
東京海上日動 プログラミングコンテスト2020
<h1 id="東京海上日動 プログラミングコンテスト2020"><a href="#%E6%9D%B1%E4%BA%AC%E6%B5%B7%E4%B8%8A%E6%97%A5%E5%8B%95%E3%80%80%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%B3%E3%83%B3%E3%83%86%E3%82%B9%E3%83%882020">東京海上日動 プログラミングコンテスト2020</a></h1>
<p><a target="_blank" rel="nofollow noopener" href="https://atcoder.jp/contests/tokiomarine2020">東京海上日動 プログラミングコンテスト2020</a></p>
<p>参加日 2020/06/14</p>
<p>参加したので記録として残す。<br />
問題文はタイトルのリンク参照。</p>
<p>(コードブロックの中のシンタックスハイライト効かない?)</p>
<h1><a target="_blank" rel="nofollow noopener" href="https://atcoder.jp/contests/tokiomarine2020/tasks/tokiomarine2020_a">A</a></h1>
<p>見たまま。<br />
どこでも良いので先頭から3文字にした</p>
<pre><code class="Python">S = input()
print(S[:3])
</code></pre>
<p>Shortest目指して1行で書くならこうだったなと。</p>
<pre><code>print(input()[:3])
</code></pre>
<p>早ければ早いだけ良かったので速度重視ということで。<br />
今回は問題開く時間, 問読み時間も含めて<em>27</em>秒で行けた。だいぶいい感じ。</p>
<h1><a target="_blank" rel="nofollow noopener" href="https://atcoder.jp/contests/tokiomarine2020/tasks/tokiomarine2020_b">B</a></h1>
<p>追う側が追われるより早い必要があってさらに時間内に追いつければいい。<br />
ということは<code>速度の差 / 距離の差 <= 制限時間</code>とすればいい。</p>
<p>距離の差が0になっちゃうと動かないのでその条件も追加した。</p>
<pre><code class="Python">A,V = map(int, input().split())
B,W = map(int, input().split())
T = int(input())
print('YES' if V-W != 0 and 0 <= abs(A-B)/(V-W) <= T else 'NO')
</code></pre>
<p>もっと短くエラー無く書こうとするなら割り算を消した式(<code>速度の差 <= 制限時間 * 距離の差</code>)に変えればよかった。</p>
<pre><code>A,V = map(int, input().split())
B,W = map(int, input().split())
T = int(input())
print('YES' if abs(A-B) <= (V-W)*T else 'NO')
</code></pre>
<p>問題解いてる量が多いほど誤読したかも?<br />
1秒毎に今の位置から<code>V</code>,<code>W</code>だけ動くって読んじゃうと追い越しちゃったらどうするんだ的な疑問が出てきてハマってたかも。怖い。</p>
<h1><a target="_blank" rel="nofollow noopener" href="https://atcoder.jp/contests/tokiomarine2020/tasks/tokiomarine2020_c">C</a></h1>
<p>今まで本番で解けなかった500点問題。初めて解けたのが嬉しすぎてこのブログ開設したきっかけになった。</p>
<h2 id="最初の考察"><a href="#%E6%9C%80%E5%88%9D%E3%81%AE%E8%80%83%E5%AF%9F">最初の考察</a></h2>
<ol>
<li><p>愚直にやるなら?</p>
<ul>
<li><code>N <=10^5</code>, <code>K<=10^5</code></li>
<li>次のステップでランプが何個のランプから照らされているかを先頭から見るのをK回繰り返す方法だとどうか。
<ul>
<li><code>O(N*N*K)</code>になる</li>
<li>愚直な実装だとまず間に合わない</li>
</ul></li>
<li>どうやったら計算量減らせるかを考えよう</li>
</ul></li>
<li><p>法則性を探してみる</p>
<ul>
<li>サンプルから探す
<ul>
<li>0も次のステップでは1以上になることに気づく(自分自身で照らしているってことかなと納得)</li>
<li>次のステップで照らされる数が減ることはないことに気づく</li>
<li>照らされる数は最大でもランプの数(N)なことに気づく
<ul>
<li>つまり<code>[N,N,...N]</code>になったらそれ以上変化しない</li>
</ul></li>
<li>実験してみる</li>
</ul></li>
</ul></li>
</ol>
<h2 id="実験コード"><a href="#%E5%AE%9F%E9%A8%93%E3%82%B3%E3%83%BC%E3%83%89">実験コード</a></h2>
<pre><code class="Python">N, K = map(int, input().split())
A_list = np.array(list(map(int, input().split())))
# 愚直にやってパターンを見つけてみる
for k in range(K):
B_list = []
for i in range(N):
tmp = 0
for j in range(N):
tmp += 1 if A_list[j] >= abs(i-j) else 0
B_list.append(tmp)
print(k+1, B_list)
A_list = B_list
</code></pre>
<h4 id="実行1"><a href="#%E5%AE%9F%E8%A1%8C1">実行1</a></h4>
<p>サンプルケースと同じNとAのリストで4回回してみる。</p>
<pre><code>1 [1, 2, 2, 1, 2]
2 [3, 3, 4, 4, 3]
3 [4, 5, 5, 5, 4]
4 [5, 5, 5, 5, 5]
</code></pre>
<p>1でサンプル1の結果と合っている。<br />
2でサンプル2の結果と合っているので動きとしては正しそう。</p>
<h4 id="実行2"><a href="#%E5%AE%9F%E8%A1%8C2">実行2</a></h4>
<p>Nを大きくして更にパターンが見えないか試す。</p>
<pre><code># 入力
# N = 100, K = 100
# A_list = [0]*100
# A_list[0] = 1
</code></pre>
<pre><code>1 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ... [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
2 [2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] ... [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2]
3 [4, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7] ... [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 4]
4 [8, 9, 10, 11, 12, 12, 13, 13, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15] ... [15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 13, 13, 12, 12, 11, 10, 9, 8]
5 [16, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 28, 29] ... [29, 28, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16]
...
11 [95, 96, 97, 97, 98, 98, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 98, 98, 97, 97, 96, 95]
12 [98, 98, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 98, 98]
13 [99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99]
14 [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
...
99 [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
100 [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100] ... [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
</code></pre>
<p>14回の時点で収束している。</p>
<p>更に試して<code>N=10</code>の時は<code>6</code>回, <code>N=100</code>の時は14回, <code>N=1000</code>の時は<code>22</code>回なことが分かった。<br />
<code>N=10000</code>以上は手元の環境だと遅すぎ終わるのが待てなかったがおそらく<code>N=10000</code>で<code>30</code>回程度, <code>N=100000</code>で<code>50</code>回未満と予想がついた。</p>
<h2 id="考察2"><a href="#%E8%80%83%E5%AF%9F2">考察2</a></h2>
<ul>
<li>Kステップ繰り返す部分は最大でも50回程度やれば十分なことがわかったので計算量が<code>O(N*N*50)</code>程度にできて計算量減らせた嬉しい。</li>
<li>しかしまだ遅いのでN*Nの部分を減らしたい</li>
<li><code>imos法</code>が使えそう</li>
<li>先頭からと後ろから2回imos法やればO(2<em>N</em>40)程度にできるのでは?
<ul>
<li>片方向に動くからすでに書き換えている値を更に書き換えるのは遅そうだなと思っての発想</li>
</ul></li>
<li>A[i] = iより前のランプで照らされる数 + iより後のランプで照らされる数 + 1</li>
</ul>
<h2 id="実装"><a href="#%E5%AE%9F%E8%A3%85">実装</a></h2>
<pre><code>def main():
N, K = map(int, input().split())
A_list = list(map(int, input().split()))
for _ in range(min(K,50)):
e_list_1 = [0] * N
l_1 = [0] * N
v = 0
for i,a in enumerate(A_list):
l_1[i] = v
e_list_1[min(i + a, N-1)] += 1
v = v + 1 - e_list_1[i]
e_list_2 = [0] * N
v = 0
for _i,a in enumerate(A_list[::-1]):
i = N-1-_i
A_list[i] = l_1[i] + v + 1
e_list_2[max(i-a, 0)] += 1
v = v + 1 - e_list_2[i]
print(*A_list)
if __name__ == "__main__":
main()
</code></pre>
<p>Python3だとTLE出したのでPyPy使ったらACできた。嬉しすぎて夜中なのに叫んじゃった。<br />
コンテスト中にPythonで通してる人3人しかいなかった。すごすぎ</p>
<h1 id="最後"><a href="#%E6%9C%80%E5%BE%8C">最後</a></h1>
<ul>
<li>コンテスト中に500点問題ACできるのマジで嬉しい</li>
<li>無事に水パフォ出てレート増えた嬉しい</li>
<li>区間数えるみたいなのはimos法使うと良いね</li>
<li>もっと上の難易度になるとPythonだと無理になりそう</li>
</ul>
pantz