의 공간을 or 타일로 채우는 방법의 갯수를 구하는 문제
제한 시간 : 8초 (for python 3)
제한 메모리 : 1056 MB (for python 3)


n의 제한이 이므로, 이 점화식으로 다이나믹 프로그래밍을 사용하기엔 시간 제한적으로 무리가 상당히 따른다. 따라서, 분할 정복을 통한 빠른 거듭제곱을 위해 점화식을 바탕으로 한 행렬을 세팅하자.
= ... (A-5)
으로 세팅을 하게 되면,
= ... (A-6)
이 되게 된다.
이 때, A(1) = 3, A(0) = 1로 설정을 하자. (because A(2) = 11)
= ... (A-8)
가 된다.
python의 경우 numpy를 통해 행렬의 곱셈을 간단하게 할 수 있지만, 이 상황의 경우 numpy를 사용할 수 없으므로, 행렬의 곱셈을 정의해주어야한다.
이를 하고 나면, 행렬을 제곱한 리스트를 만들고, n을 이진수로 바꿔준 다음, 자릿수가 1인 부분에 해당하는 행렬()을 곱해주면 원하는 값이 나오게 된다. 이때, 점화식대로 행렬을 곱하다 원소가 음수가 될 수도 있으니, % 연산을 쓰기 전에 따로 처리를 해준다.
# 13976 타일 채우기 2
N = int(input())
mod = 1000000007
# 2*2 행렬 곱셈 정의
def matrix_multiply(
matrix1: list[list[int]], matrix2: list[list[int]]
) -> list[list[int]]:
new_matrix = [[0 for i in range(2)] for i in range(2)]
for i in range(2):
for j in range(2):
new_matrix_element = 0
for k in range(2):
new_matrix_element += matrix1[i][k] * matrix2[k][j]
if new_matrix_element >= 0:
new_matrix[i][j] = new_matrix_element % mod
else:
new_matrix[i][j] = -1 * ((-1 * new_matrix_element) % mod)
return new_matrix
# 2*2 행렬 제곱 정의
def matrix_square(matrix: list[list[int]]) -> list[list[int]]:
return matrix_multiply(matrix, matrix)
if N % 2:
print(0)
else:
N //= 2
# a(0) = 1, S(1) = 3
# a(n+2) = 3*a(n+1)+2*S(n)
# a(n+2) = 4*a(n+1)-a(n)
matrix = [[4, -1], [1, 0]]
# A**(2**n)에 대한 행렬을 세팅해두기
matrix_powered_dict = dict()
matrix_powered_dict[0] = matrix
initial_matrix = [3, 1]
for i in range(64):
matrix_powered_dict[i + 1] = matrix_square(matrix_powered_dict[i])
binary_index = [N // (2**i) % 2 for i in range(64)]
# 2*2 단위 행렬 세팅
final_matrix = [[1 if i == j else 0 for i in range(2)] for j in range(2)]
# binary_index의 원소 중 1인 것들만 곱하기
for k, j in enumerate(binary_index):
if j:
final_matrix = matrix_multiply(final_matrix, matrix_powered_dict[k])
# 마지막으로 [3, 1] 곱하기
final_value_list = [0, 0]
for k in range(2):
for j in range(2):
final_value_list[j] += final_matrix[j][k] * initial_matrix[k]
result = final_value_list[1] % mod
print(result)