📜  门|门 CS 1999 |第 66 题(1)

📅  最后修改于: 2023-12-03 15:42:21.106000             🧑  作者: Mango

门|门 CS 1999 | 第 66 题介绍

题目描述

此题目为“置换矩阵”的题目。

给定一个 $n$ 阶置换矩阵 $A$。按顺序询问 $m$ 次,每次输入一个矩阵 $B$,需要求出 $AB$。矩阵大小为 $n \times n$,矩阵元素均为 $0$ 或 $1$。

答案对 $10^9+7$ 取模,结果输出到标准输出。

题解思路

由于本题的答案对 $10^9 + 7$ 取模,所以考虑使用矩阵快速幂求解。

首先将题意转换为:给出 $n$ 个初始位置 $p_1,p_2,...,p_n$ 和 $n$ 个最终位置 $q_1,q_2,...,q_n$,表示在 $B$ 矩阵上,原来在 $i$ 行 $j$ 列的元素现在在 $p_i$ 行 $p_j$ 列,问最终所在位置为 $q_i$ 行 $q_j$ 列的元素值是多少。

我们可以枚举最终位置,也就是枚举 $q_k$,然后统计有多少对初始位置 $(i,j)$ 满足 $(p_i,p_j) = (q_k,q_l)$。设这个数量为 $x_k$,则最终矩阵的第 $k$ 行 $l$ 列元素的值就是 $x_k$。

将问题转化之后,我们发现矩阵乘法就是我们要计算的函数。

设 $f(i,j)$ 表示原来在 $i$ 行 $j$ 列的元素最后停留在 $k$ 行 $l$ 列的方案数,则有 $f(k,l) = \sum\limits_{i=1}^n \sum\limits_{j=1}^n f(i,j) [p_i=k, p_j=l]$。

注意到 $f$ 是一个关于矩阵 $B$ 的函数,我们可以将这个式子写成矩阵乘法的形式,从而将矩阵快速幂算法进行应用。

最后,需要注意的是,矩阵快速幂过程中需要开一个新的矩阵用于储存答案,因为在本题中,$A$ 矩阵和 $B$ 矩阵的大小是 $n^2$,两个矩阵相乘会得到一个大小为 $n^4$ 的矩阵,如果直接更新 $B$ 矩阵,会导致内存错误。因此,我们需要同时维护两个矩阵,一个作为中间矩阵,一个作为结果矩阵。

代码实现

下面为 Python3 代码实现:

MOD = 1000000007

def multiply(A, B):
    n = len(A)
    C = [[0] * n for i in range(n)]
    for i in range(n):
        for j in range(n):
            for k in range(n):
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD
    return C

def quick_power(A, k):
    n = len(A)
    res = [[0] * n for i in range(n)]
    for i in range(n):
        res[i][i] = 1
    while k > 0:
        if k & 1:
            res = multiply(res, A)
        A = multiply(A, A)
        k >>= 1
    return res

def solve(n, m, A, query):
    # 统计初始位置和最终位置
    L = [0] * n
    R = [0] * n
    for i in range(n):
        for j in range(n):
            if A[i][j]:
                L[i] += 1
                R[j] += 1

    # 构造矩阵
    # f(i,j) 表示原来在 (i,j) 的元素现在在 (k,l) 的方案数
    f = [[0] * (n ** 2) for i in range(n ** 2)]
    for k in range(n):
        for l in range(n):
            idx = k * n + l
            for i in range(n):
                for j in range(n):
                    if A[i][j]:
                        f[i*n+j][idx] = 1

    # 统计答案
    B = quick_power(f, m)
    ans = [0] * (n ** 2)
    for k in range(n):
        for l in range(n):
            idx = k * n + l
            for i in range(n):
                for j in range(n):
                    if query[k][l] and query[i][j]:
                        p = i * n + j
                        x = B[p][idx]
                        ans[idx] = (ans[idx] + x) % MOD

    # 输出答案
    for i in range(n):
        for j in range(n):
            print(ans[i*n+j], end=' ')
        print()

返回的 markdown 代码片段为:

```python
MOD = 1000000007

def multiply(A, B):
    n = len(A)
    C = [[0] * n for i in range(n)]
    for i in range(n):
        for j in range(n):
            for k in range(n):
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD
    return C

def quick_power(A, k):
    n = len(A)
    res = [[0] * n for i in range(n)]
    for i in range(n):
        res[i][i] = 1
    while k > 0:
        if k & 1:
            res = multiply(res, A)
        A = multiply(A, A)
        k >>= 1
    return res

def solve(n, m, A, query):
    # 统计初始位置和最终位置
    L = [0] * n
    R = [0] * n
    for i in range(n):
        for j in range(n):
            if A[i][j]:
                L[i] += 1
                R[j] += 1

    # 构造矩阵
    # f(i,j) 表示原来在 (i,j) 的元素现在在 (k,l) 的方案数
    f = [[0] * (n ** 2) for i in range(n ** 2)]
    for k in range(n):
        for l in range(n):
            idx = k * n + l
            for i in range(n):
                for j in range(n):
                    if A[i][j]:
                        f[i*n+j][idx] = 1

    # 统计答案
    B = quick_power(f, m)
    ans = [0] * (n ** 2)
    for k in range(n):
        for l in range(n):
            idx = k * n + l
            for i in range(n):
                for j in range(n):
                    if query[k][l] and query[i][j]:
                        p = i * n + j
                        x = B[p][idx]
                        ans[idx] = (ans[idx] + x) % MOD

    # 输出答案
    for i in range(n):
        for j in range(n):
            print(ans[i*n+j], end=' ')
        print()