【FZU 1911】Construct a Matrix(矩阵快速幂+找规律)

传送门:http://acm.fzu.edu.cn/problem.php?pid=1911

题意:数列fn为斐波那契数列,sn为fn的前n项和,设r=sn%m,求一个r*r的矩阵,矩阵元素只能为0,-1,1使得矩阵每行每列的和没有重复。

分析:我们知道fibonacci数列的前n项和S(n)=2*F(n)+F(n-1)-1,而本题中n的取值范围很大,所以求前n项和需要用矩阵快速幂。本题难点在于如何构造一个矩形满足题中条件。

首先我们分析一下,显然当r为0时,我们构造不出来这样一个矩阵,所以矩阵不存在;当r为1时,无论这个格子填什么数,他所在的行和列的和一定相同。

我们可以推广到r为奇数时,也必有一行和一列的和相等,所以当r==1||r&1时矩阵式不存在的。

当r为偶数,假设r==6,此时共有12个和,范围是-6~6,其*有6个奇数,7个偶数。我们先把矩阵初始化为-1,可知,每将一个-1变为0,就会使两个偶数和变为奇数和(当任意两个0不同行或同列时),

所以至少要有3个不同行同列的-1变为0,才能使得和为6奇6偶。不妨假设现有矩阵为:

-1 -1 -1 -1 -1 -1

-1 -1 -1 -1 -1 -1

-1 -1 -1 -1 -1 -1

 0 -1 -1 -1 -1 -1

-1  0 -1 -1 -1 -1

-1 -1  0 -1 -1 -1

接下来我们就要构造3个0所在的行和列,使他们的和分别等于6个不同的奇数:

-1 -1 -1

-1 -1 -1

-1 -1 -1

 0 -1 -1  1  1  1

 1  0 -1  1  1  1

 1  1  0  1  1  1

这样,对于剩余的右上角的9个元素,我们只要填1和-1使得剩下的行和列的和为7个偶数中的任意6个即可(对称构造即可):

-1 -1 -1 -1 -1  1

-1 -1 -1 -1  1  1

-1 -1 -1  1  1  1

 0 -1 -1  1  1  1

 1  0 -1  1  1  1

 1  1  0  1  1  1

这样,一个满足条件的矩阵就构造出来了。

总结:本题也属于思维大于算法和代码的题,难点在于如何去构造矩阵。

代码如下:

///BY: Torrance_ZHANG
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
int m, n;
struct Matrix {
    int a[2][2];
    Matrix() {
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                a[i][j] = 0;
    }
    Matrix operator*(Matrix &p) {
        Matrix res;
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                res.a[i][j] = 0;
                for (int k = 0; k < 2; k++) {
                    res.a[i][j] += (a[i][k] * p.a[k][j] % m);
                }
                res.a[i][j] %= m;
            }
        }
        return res;
    }
} init, unit;
void init_Matrix() {
    init.a[0][0] = 1;
    init.a[0][1] = 0;
    init.a[1][0] = 0;
    init.a[1][1] = 1;
    unit.a[0][0] = 1;
    unit.a[0][1] = 1;
    unit.a[1][0] = 1;
    unit.a[1][1] = 0;
}
Matrix Pow(Matrix x, Matrix y, int k) {
    while (k) {
        if (k & 1)
            y = y * x;
        x = x * x;
        k >>= 1;
    }
    return y;
}
void solve(int r) {
    printf("Yes
");
    int ans[205][205];
    memset(ans, -1, sizeof(ans));
    for (int i = r / 2 + 1; i <= r; i++)
        ans[i][i - (r / 2)] = 0;
    for (int i = r / 2 + 2; i <= r; i++)
        for (int j = 1; j <= (i - r / 2 - 1); j++)
            ans[i][j] = 1;
    for (int i = r / 2 + 1; i <= r; i++)
        for (int j = r - i + 1; j <= r; j++)
            ans[j][i] = 1;
    for (int i = 1; i <= r; i++) {
        for (int j = 1; j <= r; j++) {
            printf("%d", ans[i][j]);
            printf(j == r ? "
" : " ");
        }
    }
}
int main() {
    int t, cas = 1;
    scanf("%d", &t);
    while (t--) {
        init_Matrix();
        scanf("%d%d", &n, &m);
        printf("Case %d: ", cas++);
        Matrix tmp = Pow(unit, init, n);
        //cout<<tmp.a[0][0]<<" "<<tmp.a[0][1]<<" "<<tmp.a[1][1]<<endl;
        int r = (tmp.a[0][1] * 2 + tmp.a[1][1] - 1) % m;
        //cout<<r<<endl;
        if (r == 0 || r & 1) {
            printf("No
");
            continue;
        } else solve(r);
    }
}