selpo's diary

主に競プロの解説をします。C#erです。

問題C『+/- Rectangle』 : AtCoder Grand Contest 016

問題文 : +/- Rectangle

概要

H\times Wの盤面に整数を書き込んで、次の条件を満たすようにできるか?できるなら具体的に作れ。

  • h\times wの大きさの部分長方形を任意に取ってくると、その中での総和は必ず負
  • 盤面全体の総和は正
制約

1\leq h\leq H\leq 500
1\leq w\leq W\leq 500

解法

全体を部分長方形で埋めつくせれば明らかに不可能。
そうでないとき、H\bmod h\neq 0またはW\bmod w\neq 0である。必要に応じて転置し、以下ではH\bmod h\neq 0とする。
1次元の場合、つまり、

長さHの数列で、全体の総和は正であり、長さhの連続する部分列の総和はいずれも負である

が解けたとする。この数列をW列だけ並べれば元の問題の解になっている。
例えば、H=3,h=2,W=4,h=2のとき、1次元だけ考えると

3 -4 2

が答え。すると、

3 3 3 3
-4 -4 -4 -4
2 2 2 2

が答えになる。

実は、1次元の場合は、以下のように必ず解を持つ。

H=Nh+r, s=h-rとする(0\lt r\lt h)。長さhの数列をa,\ldots,a,-1,\ldots,-1とする(a\in\mathbb{Q}r個並べ、-1s個並べる)。
これをN個並べて、後ろにx\in\mathbb{Q}r個並べて、長さHの数列にする。
x\lt aとして、a,xをうまく調節すると解が作れる。

具体的には、H=11,h=3のとき、11=3\times 3+2

a a -1 a a -1 a a -1 x x

のようになる。このとき、守るべき条件は2a-1\lt 0\land a+x-1\lt 0\land 2x-1\lt 0\land 3(2a-1)+2x\gt 0だが、
x\lt aを仮定すると簡単になって、x\lt a\land 2a-1\lt 0\land 3(2a-1)+2x\gt 0でよい。
これは、例えばx=\frac{1}{4},a=\frac{7}{16}とするとよい。実際には整数を並べるので、

7 7 -16 7 7 -16 7 7 -16 4 4

が解。

一般化して、

x\lt a\land ra-s\lt 0\land N(ra-s)+rx\gt 0

を解けばよく、
aだけ見ると\frac{Ns}{(N+1)r}\lt a\lt\frac{s}{r}となる。よって、a=\frac{(2N+1)s}{2r(N+1)}とする。
このとき、\frac{Ns}{2r(N+1)}\lt x\lt\frac{(2N+1)s}{2r(N+1)}が必要で、x=\frac{s}{2r}とする。
あとは、分母をはらって並べれば良い。

計算量はO(WH)となる。
各要素の大きさは高々2h(N+1)\leq 501000なので、10^9を超えない。

ソースコード

using System;
using System.IO;
using System.Linq;
class Z { static void Main() { new K(); } }
class K
{
	public K()
	{
		Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
		Solve();
		Console.Out.Flush();
	}
	void Solve()
	{
		var I = Console.ReadLine().Split().Select(int.Parse).ToArray();
		int H = I[0], W = I[1];
		var t = YesNo(H, W, I[2], I[3]);
		if (t == null) Console.WriteLine("No");
		else
		{
			Console.WriteLine("Yes");
			for (var h = 0; h < H; h++)
			{
				for (var w = 0; w < W; w++) Console.Write("{0}{1}", t[h, w], w == W - 1 ? "" : " ");
				Console.WriteLine();
			}
		}
	}
	T[,] Swap<T>(T[,] t)
	{
		var Y = t.GetLength(0);
		var X = t.GetLength(1);
		var t2 = new T[X, Y];
		for (var x = 0; x < X; x++) for (var y = 0; y < Y; y++) t2[x, y] = t[y, x];
		return t2;
	}
	int[,] YesNo(int H, int W, int h, int w)
	{
		if (H % h == 0 && W % w == 0) return null;
		if (H % h == 0) return Swap(YesNo(W, H, w, h));
		int r = H % h, s = h - r, N = H / h, a = s * (2 * N + 1), b = 2 * r * (N + 1), x = s * (N + 1);
		var t = new int[H, W];
		for (var y = 0; y < W; y++)
		{
			for (var i = 0; i < N; i++)
			{
				for (var j = 0; j < r; j++) t[i * h + j, y] = a;
				for (var j = r; j < h; j++) t[i * h + j, y] = -b;
			}
			for (var i = N * h; i < H; i++) t[i, y] = x;
		}
		return t;
	}
}

サンプルがN=1の場合しかなくて、H\bmod h\neq 0じゃなくてh\lt H\lt 2hだと勘違いしていた…。