バイトの競プロメモ

主に競技プログラミング

D - Checker AtCoder Regular Contest 089

D - Checker

 

問題概略

N個の座標と希望が与えられる。

盤面をK*Kの市松模様で塗るトキ、最大でいくつの希望を叶えられるか

 

  • 1  N  105
  • 1  K  1000
  • 0  xi  109
  • 0  yi  109
  • i  j なら (xi,yi)  (xj,yj)
  • ci は B または W
  • N,K,xi,yi は整数

 

解法

2K周期で同じ色なので、4K*4Kの中にまとめられる。

白色の場合K動かして黒色にして、黒の累積和を持っておけば、K*Kの塗り方に対して希望がo(1)で求められる

 

public static void main(String[] args)
{
N = sc.nextInt();
K = sc.nextInt();
long[][] b = new long[2 * K][2 * K];
for (int i = 0; i < N; i++)
{
int x = sc.nextInt();
int y = sc.nextInt();
char c = sc.next().charAt(0);
if (c == 'W') y += K;
x %= 2 * K;
y %= 2 * K;
b[y][x]++;
}
long maxRes = 0;
RectangleSum rec = new RectangleSum(b);
//黒を動かすことだけ考える。
//シロをうごかしたさいの数はN-それ
for (int mh = 0; mh < K; mh++)
{
for (int mw = 0; mw < K; mw++)
{
//左上
long res1 = rec.getSum(0, mw, 0, mh);
//左下
res1 += rec.getSum(0, mw, K + mh, 2 * K);
//右上
res1 += rec.getSum(K + mw, 2 * K, 0, mh);
//右下
res1 += rec.getSum(K + mw, 2 * K, K + mh, 2 * K);
//真ん中
res1 += rec.getSum(mw, K + mw, mh, K + mh);
long res2 = N - res1;
maxRes = Math.max(maxRes, res1);
maxRes = Math.max(maxRes, res2);
}
}
System.out.println(maxRes);
}

//値を渡す際は半開区間
static class RectangleSum
{
//半開区間 0は0
static long[][] rui;
static int H, W;

RectangleSum(long[][] ori)
{
H = ori.length;
W = ori[0].length;
rui = new long[H + 1][W + 1];
for (int hi = 0; hi < H; hi++)
{
for (int wi = 0; wi < W; wi++)
{
rui[hi + 1][wi + 1] = ori[hi][wi];
}
}
for (int hi = 1; hi < H + 1; hi++)
{
for (int wi = 1; wi < W + 1; wi++)
{
rui[hi][wi] += rui[hi - 1][wi];
rui[hi][wi] += rui[hi][wi - 1];
rui[hi][wi] -= rui[hi - 1][wi - 1];
}
}
}

//半開区間
public static long getSum(int left, int right, int top, int bottom)
{
long res = rui[bottom][right];
res -= rui[top][right];
res -= rui[bottom][left];
res += rui[top][left];
return res;
}

}