ACM模版

描述

题解

这个问题十分有趣,因为我做不出来!!!

去年河南 ACM 省赛的第二道题,当时耽搁了我好久好久时间依然无果,最后只好作罢,放了好久没有补,今天忽然想起来,看了看代码,发现并不能完全理解,但是知道这道题肯定是递推找规律的,然而我就是静不下心来慢慢发掘其规律。作为一个职业马后炮,需要说的是,这个题可以用 dp 解,也可以用矩阵快速幂解,但是两种方法都不外乎递推找规律,那么问题来了,这个递推是什么呢?

一开始,我看了矩阵快速幂的解法,矩阵快速幂部分可以理解,但是递推部分的判断我却无法顿悟,0和1究竟表示什么?这个问题惆怅了我一下午,晚上搞来了一份神犇的题解手稿照片,看了后,顿悟啊,点赞,杠杠的,在此,为表达对神犇虔诚的敬意,故将此题解照片分享给大家,虽然不知道神犇何许人也,但是还是十分感谢他这份牛逼哄哄的题解~~~渣渣在此献上膝盖——Orz


(1)

(2)

(3)

(4)

(5)

(6)

(7)

大人真乃神人也~~~

最后,这个题用 dp 写需要用到状压 dp + 滚动数组,既费时又费钱,用矩阵快速幂搞会快很多很多,也更加经济实惠。

代码

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int MOD = 997;
const int MAXN = 16;

struct mat
{
    int A[MAXN][MAXN];

    mat()
    {
        memset(A, 0, sizeof(A));
    }

    mat operator * (const mat &a) const
    {
        mat b;
        for (int i = 0; i < MAXN; i++)
        {
            for (int j = 0; j < MAXN; j++)
            {
                for (int k = 0; k < MAXN; k++)
                {
                    b.A[i][j] += A[i][k] * a.A[k][j];
                    b.A[i][j] %= MOD;
                }
            }
        }
        return b;
    }
};

mat map;

bool cmp(int i, int j)  // 状态 j 对状态 i 是否可行 j 是 col 列的状态 i 是 col - 1 列的状态
{
    for (int row = 0; row < 4; row++)
    {
        if ((i >> row) & 1)
        {
            if ((j >> row) & 1)
            {
// if (row == 3) // 这个特判有没有都行,因为 i >> 4 肯定为 0
// { // 这样不过是思维更严谨些
// return false;
// }
                if ((i >> (row + 1)) & 1)
                {
                    i -= (1 << (row + 1));  // 等价于 i ^= 1 << (rank + 1),前边的写法不太直观
                }
                else
                {
                    return false;
                }
            }
            else
            {
                continue;
            }
        }
        else
        {
            if ((j >> row) & 1)
            {
                continue;
            }
            else
            {
                return false;
            }
        }
    }
    return true;
}

// 构造单元矩阵
void unit()
{
    for (int i = 0; i < MAXN; i++)
    {
        for (int j = 0; j < MAXN; j++)
        {
            map.A[i][j] = cmp(i, j);
        }
    }
}

int slove(int n)
{
    mat a;
    mat b = map;
    for (int i = 0; i < MAXN; i++)
    {
        a.A[i][i] = 1;
    }
    while (n)
    {
        if (n & 1)
        {
            a = a * b;
        }
        b = b * b;
        n >>= 1;
    }
    return a.A[MAXN - 1][MAXN - 1];
}

int main ()
{
    int T;
    scanf("%d", &T);

    unit();

    while (T--)
    {
        int N, M, K;
        scanf("%d%d%d", &N, &M, &K);
        printf("%d %d\n", slove(M - 1), slove(N - M - K + 1));
    }
    return 0;
}