Problem:
F: グリッドの番号

問題

えびちゃんは、横 n 列、縦 2 行のグリッドに 1 から 2 \times n までの整数をちょうど 1 つずつ書き込もうとしています。

グリッドの各マスにはちょうど 1 つの整数しか書き込めません。

普通に書き込むだけでは面白くないので、以下のようなルールを定めました。

隣り合う 2 つのマスに書き込まれた整数の、差の絶対値は k 以下である。

あるマスの右にマスが存在する時、あるマスに書き込まれた整数は、右のマスに書き込まれた整数より真に小さい。

あるマスの下にマスが存在する時、あるマスに書き込まれた整数は、下のマスに書き込まれた整数より真に小さい。

ここで、隣り合う 2 つのマスとは、あるマスと上下左右の辺を共有するマスのことを表します。

このような書き込み方は何通りあるでしょうか?
答えが非常に大きくなる可能性があるので、素数 m で割った余りを出力してください。

補足

上記の 2 番目のルールと 3 番目のルールは、以下の図のように、不等号に違反しないように整数を書き込むことを要求しています。

入力形式

入力として整数が 3 つ与えられます。

n k m

制約

1 \leq n \leq 100

1 \leq k \leq 10

2 \leq m \leq 10^9+7

m は素数

出力形式

数字の書き込み方の通り数を一行に出力してください。
また、末尾の改行を忘れないように注意してください。

入力例 1

3 2 7

出力例 1

1

上記の書き込み方が条件を満たし、これ以外に条件を満たす書き込み方は存在しません。

入力例 2

5 10 11

出力例 2

9

m で割った余りを出力してください。

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
	static Scanner sc = new Scanner(System.in);
	static int N, K, M;

	public static void main(String[] args) {
		N = sc.nextInt();
		K = sc.nextInt();
		M = sc.nextInt();
		int MASK = (1 << K) - 1;
		if (N < K) {
			System.out.println(naive());
			return;
		}
		long[][][] dp = new long[2][K + 1][1 << K];
		for (int i = 0; i < (1 << K); i++) {
			int c = 0;
			boolean ok = true;
			for (int j = 0; j < K; j++) {
				if ((i & (1 << (K - 1 - j))) == 0) {
					c--;
				} else {
					c++;
				}
				if (c < 0) {
					ok = false;
					break;
				}
			}
			if (!ok) continue;
			int u = Integer.bitCount(i);
			int l = K - u;
			if (u < l) continue;
			dp[0][u - l][i] = 1;
		}
		int t = 1;
		for (int i = K; i < 2 * N; i++, t = 1 - t) {
			for (long[] a : dp[t]) {
				Arrays.fill(a, 0);
			}
			for (int j = i % 2; j <= K; j += 2) {
				int l = (i - j) / 2;
				int u = l + j;
				for (int k = 0; k < (1 << K); k++) {
					int uc = Integer.bitCount(k);
					if (0 < j && j <= uc && (l == 0 || k != MASK)) { // put lower row
						dp[t][j - 1][(k << 1) & MASK] += dp[1 - t][j][k];
						dp[t][j - 1][(k << 1) & MASK] %= M;
					}
					if (u < N && j < K && k != 0) { // put upper row
						dp[t][j + 1][((k << 1) + 1) & MASK] += dp[1 - t][j][k];
						dp[t][j + 1][((k << 1) + 1) & MASK] %= M;
					}
				}
			}
//			System.err.println(Arrays.deepToString(dp[t]));
		}
		long ans = 0;
		for (long v : dp[1 - t][0]) {
			ans += v;
		}
		System.out.println(ans % M);
	}

	static long naive() {
		ArrayList<Integer> u = new ArrayList<>();
		ArrayList<Integer> l = new ArrayList<>();
		u.add(0);
		return rec(u, l, 1);
	}

	static long rec(ArrayList<Integer> u, ArrayList<Integer> l, int cur) {
		if (cur == 2 * N) return 1;
		long ret = 0;
		if (u.size() < N) {
			u.add(cur);
			ret += rec(u, l, cur + 1);
			u.remove(u.size() - 1);
		}
		if (l.size() < u.size() && cur <= u.get(l.size()) + K) {
			l.add(cur);
			ret += rec(u, l, cur + 1);
			l.remove(l.size() - 1);
		}
		return ret % M;
	}

}