[伯俊]10830/行列二乗


# Appreciation

/*
 * Problem :: 10830 / 행렬 제곱
 *
 * Kind :: Divide And Conquer
 *
 * Insight
 * - 행렬 A 의 B 제곱
 *   max(B)=10^11 이니...
 *   A * A * A * ... * A 로는 무조건 시간초과다
 *   + 분할정복을 이용해서 거듭제곱을 해야한다
 *     A^B 에서 B 가 짝수면, A^B = A^(B/2) * A^(B/2)
 *     A^B 에서 B 가 홀수면, A^B = A^((B-1)/2) * A^((B-1)/2) * A
 *     # 이렇게 계산한다면,
 *       A^17 을 구할 때
 *       A^17 = A^8 * A^8 * A
 *       A^8  = A^4 * A^4
 *       A^4  = A^2 * A^2
 *       A^2  = A^1 * A^1
 *       A^1  = A^0 * A^0 * A
 *       로, 필요에 따라 A^17, A^8, A^4, A^2, A^1 을 구하여서
 *       훨씬 효율적으로 계산할 수 있게 된다
 *       -> 이 경우, 시간복잡도는 일일이 계산하는 O(B) 에 비해
 *          O(logB) 가 걸리게 되어 시간초과를 피할 수 있다
 *
 * Point
 * - max(B)=10^11 이므로
 *   long 자료형을 사용해야 한다
 *   + 무심코 int 자료형 사용했다가 틀렸다
 *
 * - 다음과 같은 입력이 주어졌을 때,
 *   2 1
 *   1000 1000
 *   1000 1000
 *   + 출력은 다음과 같아야 한다
 *     0 0
 *     0 0
 *     # A^B 의 각 원소를 1,000 으로 나눈 나머지를 출력해야되니
 *       문제의 설명을 자세히 읽어보면 당연한 예외처리지만...
 *       질문 검색을 통해 겨우 알아낼 수 있었다 ㅠㅠ
 *       -> 이런 예외 케이스를 찾는 눈썰미를 좀더 키워야겠다
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <vector>
#include <map>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int N;
long B;
typedef vector<vector<int>> Matrix;
map<int,Matrix> dp;
#define MOD 1000

// Set up : Functions Declaration
Matrix pow(long e);
Matrix mul(Matrix U, Matrix V);


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    cin >> N >> B;
    Matrix A(N, vector<int>(N));
    for (int i=0; i<N; i++)
        for (int j=0; j<N; j++)
            cin >> A[i][j];

    // Process
    /* 다음과 같은 입력들의 예외처리
     * 2 1
     * 1000 1000
     * 1000 1000 */
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            A[i][j] %= MOD;
        }
    }

    /* 항등행렬 */
    Matrix E(N, vector<int>(N));
    for (int i=0; i<N; i++) {
        E[i][i] = 1;
    }

    dp[0] = E;
    dp[1] = A;

    Matrix ANS = pow(B);

    // Control : Output
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            cout << ANS[i][j] << ' ';
        } cout << endl;
    }
}

// Helper Functions
Matrix pow(long e)
/* 행렬 A 의 e 제곱 반환 */
{
    if (e == 0 || e == 1) return dp[e];
    if (dp.find(e) != dp.end()) return dp[e];
    /* e 가 홀수면,
     * A^e = A^e = A^((e-1)/2) * A^((e-1)/2) * A
     * 그러나 로직 상 코드가 너무 길어지기에 다음과 두번에 걸쳐서 계산
     * A^e = A^(e-1) * A
     * A^e-1 = A^((e-1)/2) * A^((e-1)/2)
     * 즉, e 를 짝수로 만들어서 한번 더 계산해 주는 식으로 구현 */
    return dp[e] = (e%2) ? mul(pow(e-1), pow(1)) : mul(pow(e/2), pow(e/2));
}

Matrix mul(Matrix U, Matrix V)
/* 행렬 U 와 행렬 V 의 곱을 반환 */
{
    Matrix R(N, vector<int>(N, 0));
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            for (int k=0; k<N; k++) {
                R[i][j] += U[i][k] * V[k][j];
            } R[i][j] %= MOD;
        }
    }
    return R;
}