読者です 読者をやめる 読者になる 読者になる

TopCoder SRM 514 Div1 Medium: MagicalGirlLevelTwoDivOne

解法

左上のN*Mの長方形内の遇奇を考えれば良い。
i行目の和が奇数になるパターンを組み合わせて、i行目までの各列の和が最終的に奇数になるようにビットDPすれば良い。

コード

import java.util.Arrays;

public class MagicalGirlLevelTwoDivOne {

	private final int MOD = (int) 1e9 + 7;

	public int theCount(String[] input, int N, int M) {
		int H = input.length;
		int W = input[0].length();
		char[][] palette = new char[H][];
		for (int i = 0; i < H; i++) {
			palette[i] = input[i].toCharArray();
		}

		long[][] even = new long[N][M];// [i][j]を偶数とした場合のパターン数
		long[][] odd = new long[N][M];// [i][j]を奇数とした場合のパターン数
		for (int i = 0; i < N; i++) {
			Arrays.fill(even[i], 1);
			Arrays.fill(odd[i], 1);
		}

		for (int i = 0; i < H; i++) {
			for (int j = 0; j < W; j++) {
				int n = i % N;
				int m = j % M;

				if (palette[i][j] == '.') {
					odd[n][m] *= 5;
					odd[n][m] %= MOD;
					even[n][m] *= 4;
					even[n][m] %= MOD;
				} else {
					int num = palette[i][j] - '0';
					if (num % 2 == 1) {
						even[n][m] = 0;
					} else {
						odd[n][m] = 0;
					}
				}
			}
		}

		// [i][bit]:= i行目の奇遇の並びがbitのようになる時のパターン数(奇数なら1)
		long[][] transition = new long[N][1 << M];
		for (int i = 0; i < N; i++) {
			// i行目を見る
			for (int bit = 0; bit < (1 << M); bit++) {
				// i行目の和が偶数ならば条件を満たさないのでスルー
				if (Integer.bitCount(bit) % 2 == 0) {
					continue;
				}

				transition[i][bit] = 1;
				for (int j = 0; j < M; j++) {
					if (((bit >> j) & 1) == 1) {
						transition[i][bit] *= odd[i][j];
						transition[i][bit] %= MOD;
					} else {
						transition[i][bit] *= even[i][j];
						transition[i][bit] %= MOD;
					}
				}
			}
		}

		// dp[i+1][bit]:= i行目までの各列の和がbitのようになっている時のパターン数
		long[][] dp = new long[N + 1][1 << M];
		dp[0][0] = 1;
		for (int i = 0; i < N; i++) {
			for (int prevBit = 0; prevBit < (1 << M); prevBit++) {
				// i-1行目までの和がprevBitのようになっている時を考える。
				for (int nowBit = 0; nowBit < (1 << M); nowBit++) {
					// i行目がnowBitのようになっているとする
					if (Integer.bitCount(nowBit) % 2 == 0) {
						continue;
					}
					int nextBit = prevBit ^ nowBit;
					dp[i + 1][nextBit] += dp[i][prevBit]
							* transition[i][nowBit];
					dp[i + 1][nextBit] %= MOD;
				}
			}
		}

		// N-1行目までの各列の和は全て整数になってなければならないので、
		// dp[N][111...111]に求めるべき答が入っているはずである
		return (int) dp[N][(1 << M) - 1];
	}
}