Loading [MathJax]/jax/output/CommonHTML/jax.js
[[ARC 089]]

ARC 089

C - Traveling

C - Traveling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def check(cx, cy, t, x, y):
    dx = abs(x - cx)
    dy = abs(y - cy)
    if dx + dy > t:
        return False
    if ((dx + dy) & 1) != (t & 1):
        return False
    return True
 
 
n = int(input())
ct, cx, cy = 0, 0, 0
for _ in range(n):
    t, x, y = map(int, input().split())
    result = check(cx, cy, t - ct, x, y)
    if not result:
        print('No')
        break
    ct, cx, cy = t, x, y
else:
    print('Yes')

D - Checker

問題

  • 無限のXY座標平面を、グリッド K の市松模様に塗る
    • 白黒の境界位置は自由
  • 座標 (x,y) を(黒/白)に塗りたいという希望が N 個ある
  • 最大いくつの希望を叶えられるか
  • 1K1000
  • 1x,y105
K=3だと
■□□□■■      ■■□□□■
■□□□■■      ■■□□□■
□■■■□□      ■■□□□■
□■■■□□      □□■■■□
□■■■□□      □□■■■□
■□□□■■ とか □□■■■□ とかの境界位置(塗るパターン)がある

解法

計算量の見積もり

塗るパターンは、境界位置が x,y 座標に各 K 通りに、どちらを黒にするかで2通りあるので、2K2 通り。この部分は総当たりで調べるしかないと勘で仮定する。

とすると、K1000 なので、各パターンにつき O(1) で、叶えられる希望数を求めないと間に合わない。

情報をまとめる

市松模様の特徴、座標の繰り返しを利用する。

  • (x,y)=(x+kK,y+lK) 、ただし k,l 整数、偶奇一致
  • (x,y)(x+kK,y+lK) 、ただし k,l 整数、偶奇異なる

つまり、K=3 なら、希望は以下のようになる。

  • (5,3) を黒にしたい = (2,0) を黒にしたい
  • (5,2) を黒にしたい = (2,2) を白にしたい

これで、各希望の座標を 1x,y<K の範囲にまとめられた。

二次元累積和

「白にしたい」希望と「黒にしたい」希望について個別に K×K の二次元累積和テーブルを作ると、叶えられる希望数が O(1) で求められる。二次元累積和では、任意の矩形範囲にある要素数をO(1)で求められる。

二次元累積和 - ei1333's page

  • 二次元累積和
    • req_b[x][y] =(0,0)(x,y) にある「そこを黒にしたい」という希望の数
    • req_w[x][y] =(0,0)(x,y) にある「そこを白にしたい」という希望の数

すると、ある x,y を市松模様の境界とした時に叶えられる希望数は、

たとえばK=3, x,y=1,2とする

パターンA  パターンB
□■■  ■□□
■□□  □■■
■□□  □■■

パターンAで叶えられる希望数は、以下の和となる。

  • Aの図で■の範囲にある「黒にしたい」希望数
  • Aの図で□の範囲にある「白にしたい」希望数
叶えられる   ..■■   ......   ..■■
黒にしたい = ■.... = ■.... + ......
希望数       ■....   ■....   ......

..■■   ■■■   ■....   ......   ......
...... = ■■■ - ■.... - ■■■ + ■....
......   ■■■   ■....   ■■■   ■....

叶えられる   □....   □□□   ..□□
白にしたい = ..□□ = □□□ - □....
希望数       ..□□   □□□   □....
                      で、黒と同様に求められる

これを黒白別で求めた合計が、パターンAでの希望数となる。

パターンBで叶えられる希望数は、「Aで叶えられない希望数」となる。

境界位置をずらし、全パターン求め、その最大値が答え。

Python

配列を回して添え字から要素を取得する処理は、Pythonはやや苦手で遅い。しかし二次元累積和を求める際は多用する。

PyPyなら余裕を持って通るが、PythonならTLEだった。でも通してる人もいるので、方法はあるのだろう。(NumPyあたり上手く使えばいいのかな?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from itertools import accumulate
 
n, k = map(int, input().split())
k2 = k * 2
req_b = [[0] * k for _ in range(k)]
req_w = [[0] * k for _ in range(k)]
bc, wc = 0, 0
for i in range(n):
    x, y, c = input().split()
    x, y = int(x), int(y)
    rx, ry = x % k2, y % k2
    b = c == 'B'
    if rx >= k:
        rx -= k
        if ry >= k:
            ry -= k
        else:
            b = not b
    elif ry >= k:
        ry -= k
        b = not b
    if b:
        req_b[rx][ry] += 1
        bc += 1
    else:
        req_w[rx][ry] += 1
        wc += 1
 
# 累積和の計算
req_b[0] = list(accumulate(req_b[0]))
req_w[0] = list(accumulate(req_w[0]))
 
for i in range(1, k):
    req_b[i][0] += req_b[i - 1][0]
    req_w[i][0] += req_w[i - 1][0]
    for j in range(1, k):
        req_b[i][j] += req_b[i][j - 1] + req_b[i - 1][j] - req_b[i - 1][j - 1]
        req_w[i][j] += req_w[i][j - 1] + req_w[i - 1][j] - req_w[i - 1][j - 1]
 
# 各パターン走査
ans = 0
for dx in range(k):
    dxk_b = req_b[dx][-1]
    dxk_w = req_w[dx][-1]
    for dy in range(k):
        b1 = req_b[dx][dy]
        b2 = bc - req_b[-1][dy] - dxk_b + b1
        w1 = req_w[dx][dy]
        w2 = wc - req_w[-1][dy] - dxk_w + w1
        b, w = b1 + b2, w1 + w2
        ans = max(ans, b + wc - w, w + bc - b)
        # print(dx, dy, b, w, b + wc - w, w + bc - b)
print(ans)

itertools.accumulate の利用と、配列参照を少なくしたらpythonでも通った。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from itertools import accumulate
 
# 入力からreq_b,req_wを作る処理は上と同じ
 
# 累積和の計算
req_b = [list(accumulate(req)) for req in req_b]
req_w = [list(accumulate(req)) for req in req_w]
req_b = [list(accumulate(req)) for req in zip(*req_b)]
req_w = [list(accumulate(req)) for req in zip(*req_w)]
 
# 各パターン走査
ans = 0
kb, kw = req_b[-1], req_w[-1]
for dx_b, dx_w in zip(req_b, req_w):
    dxk_b = dx_b[-1]
    dxk_w = dx_w[-1]
    for b1, w1, kyb, kyw in zip(dx_b, dx_w, kb, kw):
        b = bc - kyb - dxk_b + b1 * 2
        w = wc - kyw - dxk_w + w1 * 2
        a = b + wc - w
        ans = max(ans, a, n - a)
print(ans)

programming_algorithm/contest_history/atcoder/2018/0121_arc089.txt · 最終更新: 2018/07/25 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0