Problem:
G: 運命力 - Destiny Draw -

問題

Mr. D はカードゲームで勝負をすることになった。
このカードゲームでは、 N 枚のカードの山を用いる。
また、山のカードには、上から順に 1, 2, 3, ... , N の番号がついている。

D で始まるものすべてを背負う彼に敗北は許されないが、不幸にも Mr. D はカードゲームが不得手である。
そこで、自分が引くカードをコントロールすることで勝利を手にすることにした。

Mr. D は K 種類のシャッフルが可能である。
K 種類のうち i 番目のシャッフルでは上から a_i 枚目から a_i+b_i − 1枚目までのちょうど b_i 枚を引き抜き、上に重ねる。
i番目のシャッフルを1 回するとき、それぞれ t_i 秒を要する。

ちょうど T 秒のシャッフルの後、一番上のカードを C にする方法は何通りあるか。 数が多くなる可能性があるので、 10^9+7 で割った余りを出力せよ。

入力形式

入力は次の形式で与えられる。

N K C T
a_1 b_1 t_1
a_2 b_2 t_2
...
a_K b_K t_K

N は整数で 2 \≤ N \≤ 40 を満たす。

K は整数で 1 \≤ K \≤ \frac{N (N+1)}{2} を満たす

C は整数で 1 \≤ C \≤ N を満たす

T は整数で 1 \≤ T \≤ 1,000,000 を満たす

a_i (i = 1, 2, ... , K) は整数で 1 \≤ a_i \≤ N を満たす

b_i (i = 1, 2, ... , K) は整数で 1 \≤ b_i \≤ N − a_i + 1 を満たす

a_i = a_j かつ b_i = b_j を満たすとき i = j に限定される

t_i は整数で 1 \≤ t_i \≤ 5 を満たす

出力形式

答えを 10^9+7 で割った余りを一行で出力せよ。

入力例1

4 1 1 6
3 2 3

出力例1

1

入力例2

4 1 1 5
3 2 3

出力例2

0

入力例3

6 2 2 5
1 2 1
2 5 3

出力例3

3

ちょうど 5 秒で一番上のカードを 2 にする方法は以下の 3 通りである。

1→1→2
1→2→1
2→1→1

入力例4

6 8 3 10
1 4 5
1 3 3
1 6 5
1 2 2
1 1 4
2 5 1
4 3 1
2 1 3

出力例4

3087

Solution in JAVA (judge verdict: Accepted)

import java.util.*;

public class Main {
	static final long MODULO = 1_000_000_000 + 7;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		int K = sc.nextInt();
		int C = sc.nextInt() - 1;
		int T = sc.nextInt();
		int[] a = new int[K];
		int[] b = new int[K];
		int[] t = new int[K];
		for (int i = 0; i < K; ++i) {
			a[i] = sc.nextInt();
			b[i] = sc.nextInt();
			t[i] = sc.nextInt();
		}
		long[][][] A = new long[6][N][N];
		for (int i = 0; i < K; ++i) {
			for (int j = 0; j < b[i]; ++j) {
				A[t[i]][j][(a[i] - 1) + j] += 1;
			}
			for (int j = 0; j < a[i] - 1; ++j) {
				A[t[i]][b[i] + j][j] += 1;
			}
			for (int j = a[i] + b[i] - 1; j < N; ++j) {
				A[t[i]][j][j] += 1;
			}
		}
		long[][] dp = new long[6][N];
		dp[0][C] = 1;
		for (int i = 1; i <= 5; ++i) {
			for (int j = 0; j < i; ++j) {
				dp[i] = addVec(dp[i], mulVec(A[i - j], dp[j]));
			}
		}
		long[] v = new long[N * 5];
		for (int i = 0; i < 5; ++i) {
			for (int j = 0; j < N; ++j) {
				v[j + i * N] = dp[i][j];
			}
		}
		long[][] mx = new long[5 * N][5 * N];
		for (int i = N; i < 5 * N; ++i) {
			mx[i - N][i] = 1;
		}
		for (int i = 1; i <= 5; ++i) {
			for (int j = 0; j < N; ++j) {
				for (int k = 0; k < N; ++k) {
					mx[4 * N + j][N * (5 - i) + k] = A[i][j][k];
				}
			}
		}

		v = mulVec(pow(mx, T), v);
		System.out.println(v[0]);
	}

	static long[][] mul(long[][] a, long[][] b) {
		int n = a.length;
		long[][] ret = new long[n][n];
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				for (int k = 0; k < n; ++k) {
					ret[i][j] += a[i][k] * b[k][j] % MODULO;
					ret[i][j] %= MODULO;
				}
			}
		}
		return ret;
	}

	static long[] mulVec(long[][] a, long[] v) {
		int n = v.length;
		long[] ret = new long[n];
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				ret[i] += a[i][j] * v[j] % MODULO;
				ret[i] %= MODULO;
			}
		}
		return ret;
	}

	static long[] addVec(long[] a, long[] b) {
		int n = a.length;
		long[] ret = new long[n];
		for (int i = 0; i < n; ++i) {
			ret[i] = (a[i] + b[i]);
			if (ret[i] >= MODULO)
				ret[i] -= MODULO;
		}
		return ret;
	}

	static long[][] pow(long[][] a, long n) {
		int len = a.length;
		long[][] ret = new long[len][len];
		for (int i = 0; i < len; ++i)
			ret[i][i] = 1;
		for (; n > 0; n >>= 1, a = mul(a, a)) {
			if (n % 2 == 1) {
				ret = mul(ret, a);
			}
		}
		return ret;
	}

}