AtCoder Beginner Contest 086
C.Traveling(300)
問題
https://beta.atcoder.jp/contests/arc089/tasks/arc089_a
x-y二次元平面を考える。t=0で(0,0)にいる。1秒後に(x+1,y),(x-1,y),(x,y+1),(x,y-1)のいずれかになる。t_i秒時に( x_i, y_i )にいることができるか判定せよ。
方針
t_iからt_(i+1)への遷移を考える。x,yの変化量dx = | x_(i+1) - x_i | ,dy = | y_(i+1) - y_i | の和 dx+dyが dt = | t_(i+1) - t_i | よりも大きい場合は明らかに条件を満たせない。また、2秒かけて、(x+1,y),(x-1,y) と移動すれば元に位置に留まることができる。よってそれぞれ dt % 2 = (dx + dy) % 2 が成り立つか判定すればよい。
public void solve(int testNumber, InputReader in, PrintWriter out) { int n = in.nextInt(); int[] t = new int[n+1], x = new int[n+1], y = new int[n+1]; for (int i = 1; i < n+1; i++) { t[i] = in.nextInt(); x[i] = in.nextInt(); y[i] = in.nextInt(); } for (int i = 1; i < n+1; i++) { int dt = t[i] - t[i-1]; int dx = x[i] - x[i-1]; int dy = y[i] - y[i-1]; if (dx + dy > dt) { out.println("No"); return; } if ((dt - abs(dx + dy)) % 2 == 0) { continue; } else { out.println("No"); return; } } out.println("Yes"); }
雑記
列の差分を求めれば、貪欲に求まる問題でした。
D.Checker(500)
問題
https://beta.atcoder.jp/contests/arc089/tasks/arc089_b
x,y座標上にN個の要素が与えられる。i 番目の要素はそれぞれ x_i,y_i,c_i (c_i = {'B', 'W'})である。辺Kの市松模様でx,y座標を塗る時、x_i.y_iのc_iと同じ色になるように塗られている個数の最大値を求めよ。
方針
x_i,y_iが大きい(109)ので、普通にx_i,y_iの2次元グリッド上でシミュレーションすることはできない。よく考えると、座標は2K周期になっていることがわかる。また、x_iをKだけx軸の正の方向に動かすと色が反転する。この性質を利用すると、求める内容は2K×2Kの範囲に存在するどちらかの色('B' or 'W')の個数になる。
市松模様で塗るときの最大個数は2K×2Kの範囲を(0,0)を起点にして全探索すればよい。ただしその時、各( i , j ) を起点して色の個数を求めるときに1回あたりO(K2)かかると、全体としてO(K4)となりTLEする。色の個数を求める時、事前に2次元累積和を求めておくと、それぞれの起点ごとに色の個数はO(1)で求められ、全体の計算量はO(K2)となり間に合う。
public void solve(int testNumber, InputReader in, PrintWriter out) { int n = in.nextInt(), k = in.nextInt(); int[][] mat = new int[4*k+1][4*k+1]; int[][] sum = new int[4*k+1][4*k+1]; for (int i = 0; i < n; i++) { int x = in.nextInt(); int y = in.nextInt(); String c = in.nextString(); if (c.equals("W")) { y += k; } mat[x%(2*k)][y%(2*k)]++; } for (int x = 0; x <= 4*k; x++) { for (int y = 0; y <= 4*k; y++) { mat[x][y] = mat[x%(2*k)][y%(2*k)]; } } for (int i = 0; i <= 4*k; i++) { for (int j = 0; j <= 4*k; j++) { sum[i][j] = mat[i][j]; if (j-1 >= 0) sum[i][j] += sum[i][j-1]; if (i-1 >= 0) sum[i][j] += sum[i-1][j]; if (i-1 >= 0 && j-1 >= 0) sum[i][j] -= sum[i-1][j-1]; } } int ans = 0; for (int x = 0; x <= 2*k; x++) { for (int y = 0; y <= 2*k; y++) { int tmp = 0; tmp += get(x, y, x+k-1, y+k-1, sum); tmp += get(x+k, y+k, x+2*k-1, y+2*k-1, sum); ans = Math.max(ans, tmp); } } out.println(ans); } int get(int x1, int y1, int x2, int y2, int[][] sum) { int res = sum[x2][y2]; if (x1 > 0) res -= sum[x1-1][y2]; if (y1 > 0) res -= sum[x2][y1-1]; if (x1 > 0 && y1 > 0) res += sum[x1-1][y1-1]; return res; }
雑記
以下がポイントと思われる。
- 2次元累積和
- (グリッド上の)周期性
典型の組み合わせですが、500点問題は難しいですね。