ARC 089
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 個ある
- 最大いくつの希望を叶えられるか
- 1≤K≤1000
- 1≤x,y≤105
K=3だと ■□□□■■ ■■□□□■ ■□□□■■ ■■□□□■ □■■■□□ ■■□□□■ □■■■□□ □□■■■□ □■■■□□ □□■■■□ ■□□□■■ とか □□■■■□ とかの境界位置(塗るパターン)がある
解法
計算量の見積もり
塗るパターンは、境界位置が x,y 座標に各 K 通りに、どちらを黒にするかで2通りあるので、2K2 通り。この部分は総当たりで調べるしかないと勘で仮定する。
とすると、K≤1000 なので、各パターンにつき 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) を白にしたい
これで、各希望の座標を 1≤x,y<K の範囲にまとめられた。
二次元累積和
「白にしたい」希望と「黒にしたい」希望について個別に K×K の二次元累積和テーブルを作ると、叶えられる希望数が O(1) で求められる。二次元累積和では、任意の矩形範囲にある要素数をO(1)で求められる。
- 二次元累積和
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) |