Cod sursa(job #3303607)

Utilizator TincaMateiTinca Matei TincaMatei Data 16 iulie 2025 16:11:33
Problema Al k-lea termen Fibonacci Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 12.92 kb
#include <bits/stdc++.h>

// what the fuck
template<typename T, int N>
struct NDVector { using type = std::vector<typename NDVector<T, N - 1>::type>; };
template<typename T>
struct NDVector<T, 1> { using type = std::vector<T>; };

// A tensor is essentially a vector of tensors. (or multidimensional array)
template<typename T, int N>
using Tensor = typename NDVector<T, N>::type;

/**
 * Create a multidimensional vector with the given dimension sizes.
 *
 * In particular, create_vector(N) = create_tensor(N), create_matrix(N, M) = create_tensor(N, M).
 * If you have some weird multidimensional DP, you can create the DP table by doing:
 *      dp = create_tensor(5, 5, 5, 5, 5);
 *
 * Be careful, for a large number of dimensions, this uses a lot of memory and is very cache unfriendly.
 */
template<typename T>
std::vector<T> create_tensor(int N) {
    return std::vector<T>(N);
}
template <typename T, typename... ArgTypes>
Tensor<T, sizeof...(ArgTypes) + 1> create_tensor(int N, ArgTypes... args) {
    auto under = create_tensor<T>(args...);
    return std::vector(N, under);
}

/**
 * Create a matrix of the given dimensions.
 */
template<typename T>
Tensor<T, 2> create_matrix(int N, int M) {
    return create_tensor<T>(N, M);
}

/**
 * Frequently used definitions, like Vector, Matrices, pairs of ints, pairs, triples, etc.
 */
template<typename T>
using Vector = Tensor<T, 1>; // I could use std::vector<T>, but this is just too cool.
template<typename T>
using Matrix = Tensor<T, 2>;

template<typename T1, typename T2>
using Pair = std::pair<T1, T2>;
using PairII = Pair<int, int>;
using PairLL = Pair<long long, long long>;

template<typename T1, typename T2, typename T3>
using Triple = std::tuple<T1, T2, T3>;

/**
 * Read a vector from input. Set start to 1 if you want it to be 1-indexed.
 */
template<typename T>
Vector<T> read_vector(int N, int start = 0) {
    Vector<T> v(start + N);
    for (int i = start; i < (int)v.size(); i++) {
        std::cin >> v[i];
    }
    return v;
}

/**
 * Read a matrix from input. Set start_l to make lines 1-indexed. Same thing for start_c.
 */
template<typename T>
Matrix<T> read_matrix(int N, int M, int start_l = 0, int start_c = 0) {
    Matrix<T> matr = create_matrix<T>(N + start_l, M + start_c);

    for (int l = start_l; l < N + start_l; l++)
        for (int c = start_c; c < M + start_c; c++)
            std::cin >> matr[l][c];

    return matr;
}

/**
 * Print a tensor to the output stream. Prints all indices between i and j, and the elements 
 * are separated by the given separator.
 *
 * To generalize, for each dimension, you give the bounds that you want to print and the separator
 * between each order. To print a matrix, you would do:
 *
 *      print_tensor(matr, std::cout, 0, N - 1, "\n", 0, M - 1, " ");
 */
template<typename T>
void print_tensor(Tensor<T, 1>& tens, std::ostream&fout, int i, int j, const char* sep) {
    for (int t = std::max(i, 0); t <= j && t < (int)tens.size(); t++) {
        fout << tens[t];
        if (t + 1 <= j)
            fout << sep;
    }
}

template<typename T, typename... Sizes>
void print_tensor(
        Tensor<T, sizeof...(Sizes) / 3 + 1>& tens,
        std::ostream& fout, 
        int i, int j, const char* sep, Sizes... sizes) {
    for (int t = std::max(i, 0); t <= j && t < (int)tens.size(); t++) {
        print_tensor<T>(tens[t], fout, sizes...);
        if (t + 1 <= j)
            fout << sep;
    }
}

/**
 * Print a vector to the given output stream with given bounds and separator.
 */
template<typename T>
void print_vector(std::vector<T>& v, std::ostream& fout, int i = 0, int j = (1 << 30), const char* sep = " ") {
    print_tensor<T>(v, fout, i, j, sep);
}

/**
 * Read a vector of pairs. Set start to 1 if you want this to be 1-indexed.
 */
template<typename T1, typename T2>
Vector<Pair<T1, T2>> read_pairvec(int N, int start = 0) {
    Vector<Pair<T1, T2>> input = Vector<Pair<T1, T2>>(start + N);
    for (int i = start; i < start + N; i++)
        std::cin >> input[i].first >> input[i].second;
    return input;
}

/**
 * Read a vector of triples. Set start to 1 if you want this to be 1-indexed.
 *
 * If you need higher order tuples, like quadruples, you're better off using a matrix instead.
 */
template<typename T1, typename T2, typename T3>
Vector<Triple<T1, T2, T3>> read_triplevec(int N, int start = 0) {
    Vector<Triple<T1, T2, T3>> input = Vector<Triple<T1, T2, T3>>(start + N);
    for (int i = start; i < N + start; i++) {
        T1 a;
        T2 b;
        T3 c;
        std::cin >> a >> b >> c;
        input[i] = {a, b, c};
    }
    return input;
}

/**
 * Removes duplicates from vector. Assumes it is sorted.
 */
template<typename T>
void deduplicate(Vector<T>& v) {
    v.resize(std::unique(v.begin(), v.end()) - v.begin());    
}

/**
 * Solve a testcase of the problem. You will code your solution here instead of main.
 */
void solve_test();

/**
 * Call this function if you have a problem with multiple testcases.
 */
void multitest_problem() {
    int T;
    std::cin >> T;

    while (T--) solve_test();
}

int main() {
    std::cin.tie(NULL);
    std::iostream::sync_with_stdio(false);

    // Choose one of the following functions, depending on the problem type.
    solve_test();
    //multitest_problem();

    return 0;
}

/******************************************** modulo.h ******************************************/

/**
 * Classic modulos. The third one is not const in case you need dynamic modulo operations.
 */
const int MOD = 666013;
// const int MOD = 998244353;
//const int MOD = 1000000007;
//int MOD = 1234;

/**
 * Modulo class that encapsulates all the modulo operations. Addition, multiplication, subtraction.
 */
struct Modulo {
    int val;
    
    Modulo(long long v = 0) { v %= MOD; if (v < 0) v += MOD; val = v; }
    
    Modulo& operator+=(Modulo const& x) { 
        val = val + x.val;
        if (val >= MOD)
            val = val - MOD;
        return *this;
    }
    Modulo& operator-=(Modulo const& x) { 
        val = val - x.val;
        if (val < 0)
            val = val + MOD;
        return *this;
    }
    Modulo& operator*=(Modulo const& x) { 
        val = (long long)val * x.val % MOD;
        return *this;
    }

    friend Modulo operator+(Modulo a, Modulo const b) { return a += b; }
    friend Modulo operator-(Modulo a, Modulo const b) { return a -= b; }
    friend Modulo operator-(Modulo const b) { return 0 - b; }
    friend Modulo operator*(Modulo a, Modulo const b) { return a *= b; }
    friend std::ostream& operator<<(std::ostream& fout, Modulo const x) { return fout << x.val; }

    friend bool operator==(Modulo const a, Modulo const b) { return a.val == b.val; }
    friend bool operator!=(Modulo const a, Modulo const b) { return a.val != b.val; }
};

/**
 * Binary exponentiation of modulo numbers.
 */
Modulo lgput(Modulo b, long long e) {
    Modulo ac = 1;
    while (e > 0) {
        if (e % 2 == 1)
            ac = ac * b;
        b = b * b;
        e /= 2;
    }
    return ac;
}

/**
 * Compute the inverse of the given modulo number.
 * 
 * XXX: MOD must be a prime number. Otherwise you have to do some crazy stuff.
 */
Modulo inverse(Modulo a) {
    return lgput(a, MOD - 2);
}


/************************************* linear-algebra.h *********************************/ 

/**
 * Matrix data structure used for linear algebra.
 */
template<typename T>
struct LinMatrix {
    std::vector<std::vector<T>> val;

    LinMatrix(int N, int M): val(N, std::vector<T>(M)) {}
    LinMatrix(int N): LinMatrix(N, N) {}
    LinMatrix(): LinMatrix(0, 0) {}
    LinMatrix(std::vector<std::vector<T>>& val): val(std::move(val)) {}

    /**
     * Return sizes of the matrix.
     */
    std::pair<int, int> get_sizes() const {
        int N = val.size();
        if (N == 0) return {0, 0};
        return {N, val[0].size()};
    }

    /**
     * Standard matrix operations. Addition, subtraction, multiplication.
     *
     * If you need another underlying data structure, you do not have to implement all operations.
     */
    LinMatrix<T>& operator+= (const LinMatrix<T>& b) {
        assert(get_sizes() == b.get_sizes());
        auto [n, m] = get_sizes();
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                val[i][j] = val[i][j] + b.val[i][j];
        return *this;
    }

    LinMatrix<T>& operator-= (const LinMatrix<T>& b) {
        assert(get_sizes() == b.get_sizes());
        auto [n, m] = get_sizes();
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                val[i][j] = val[i][j] - b.val[i][j];
        return *this;
    }

    friend LinMatrix<T> operator+ (const LinMatrix<T>& a, const LinMatrix<T>& b) {
        auto res = a;
        res += b;
        return res;
    }

    friend LinMatrix<T> operator* (const LinMatrix<T>& a, const LinMatrix<T>& b) {
        auto [N, M] = a.get_sizes();
        auto [M2, K] = b.get_sizes();

        assert(M == M2);

        LinMatrix res(N, K);

        for (int l = 0; l < N; l++)
            for (int c = 0; c < K; c++)
                for (int k = 0; k < M; k++)
                    res.val[l][c] = res.val[l][c] + a.val[l][k] * b.val[k][c];
        return res;
    }

    LinMatrix<T>& operator *= (const LinMatrix& a) {
        auto res = (*this) * a;
        val = std::move(res.val);
        return *this;
    }

    /**
     * Complete matrix elements line by line, from left to right, as long as there is enough place. Good for manual testing
     * 
     * For instance:
     *      LinMatrix<int> x(3);
     *      x = {1, 2, 3, 4, 5, 6, 7}; // first two rows look as you would expect
     *                                 // last row is {7, 0, 0};
     */
    LinMatrix<T>& operator= (std::vector<T> v) {
        auto [N, M] = get_sizes();
        int p = 0;
        for (int l = 0; l < N; l++)
            for (int c = 0; c < M; c++) {
                if (p < (int)v.size())
                    val[l][c] = v[p++];
                else
                    val[l][c] = 0;
            }
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& fout, LinMatrix<T>& v) {
        auto [N, M] = v.get_sizes();

        for (int l = 0; l < N; l++) {
            for (int c = 0; c < N; c++)
                fout << v.val[l][c] << " ";
            fout << "\n";
        }
        return fout;
    }
};

/**
 * Create the identity matrix of size N.
 */
template<typename T>
LinMatrix<T> identitymatrix(int N, T e = 1, T n = 0) {
    LinMatrix<T> res(N);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            if (i == j)
                res.val[i][j] = e;
            else
                res.val[i][j] = n;
    return res;
}

/**
 * Create the null matrix of size N.
 */
template<typename T>
LinMatrix<T> nullmatrix(int N, T n = 0) {
    LinMatrix<T> res(N);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            res.val[i][j] = n;
    return res;
}

/**
 * Logarithmic exponentiation.
 */
template<typename T>
LinMatrix<T> lgput(LinMatrix<T> A, int e, T neutral = 1, T null = 0) {
    auto [N, M] = A.get_sizes();
    assert(N == M);

    LinMatrix<T> ac = identitymatrix(N, neutral, null);

    while (e > 0) {
        if (e % 2 == 1)
            ac = ac * A;
        A = A * A;
        e /= 2;
    }
    
    return ac;
}

/**
 * Given a matrix, split it into multiple matrices, each being just a row.
 */
template<typename T>
std::vector<LinMatrix<T>> split_into_rows(LinMatrix<T>& M) {
    std::vector<LinMatrix<T>> res;

    auto [R, C] = M.get_sizes();
    for (int i = 0; i < R; i++) {
        std::vector<std::vector<T>> row = {M.val[i]};
        res.push_back(LinMatrix<T>(row));
    }
    
    return res;
}

/**
 * Similarly, given a bunch of rows, merge them into a single matrix.
 */
template<typename T>
LinMatrix<T> join_rows(std::vector<LinMatrix<T>>& rows) {
    std::vector<LinMatrix<T>> res;

    int M = rows[0].get_sizes().second;

    std::vector<std::vector<T>> val;
    for (int i = 0; i < (int)rows.size(); i++) {
        auto [Np, Mp] = rows[i].get_sizes();

        assert(Np == 1); // all rows must be actually rows.
        assert(Mp == M); // all rows must have the same size.

        val.push_back(rows[i].val[0]);
    }


    return LinMatrix<T>(val);
}

/**
 * Use this example to do Fibonacci. You have a bunch of rows
 * that you use like normal variables to build your linear recurrence, and
 * then you create your matrix.
 */
LinMatrix<Modulo> build_recurrence() {
    LinMatrix<Modulo> I = identitymatrix<Modulo>(2);
    auto rows = split_into_rows(I);

    auto fn = rows[0];
    auto fn1 = rows[1];

    auto fn2 = fn + fn1;

    fn = fn1;
    fn1 = fn2;

    Vector<LinMatrix<Modulo>> M = {fn, fn1};

    return join_rows(M);
}

void solve_test() {
    // int64_t N;
    // int M;
    // std::cin >> N >> M;
    std::ifstream fin("kfib.in");
    std::ofstream fout("kfib.out");

    int K;
    fin >> K;

    auto rec = build_recurrence();
    rec = lgput(rec, K);
    auto fib = LinMatrix<Modulo>(2, 1);
    fib = std::vector<Modulo>{0, 1};

    fib = rec * fib;
    
    fout << fib.val[0][0];

    fin.close();
    fout.close();
}