P8624 [蓝桥杯 2015 省 AB] 垒骰子

这道题的数据范围比较突出:

1<=N<=1e9

先写一个O(N)算法:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;

const int mod = 1e9 + 7;

int n, m, g[8][8], f[8], op[8], bf[8];



signed main()
{
    op[1] = 4;
    op[4] = 1;
    op[2] = 5;
    op[5] = 2;
    op[3] = 6;
    op[6] = 3;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        cin >> x >> y;
        g[x][y] = g[y][x] = 1;
    }
    for(int i = 1; i <= 6; i++)
        f[i] = 4;
    for (int i = 2; i <= n; i++)
    {
        memcpy(bf, f, sizeof(f));
        for (int j = 1; j <= 6; j++)
        {
            int op_j = op[j], tmp = 0;
            for(int k = 1; k <= 6; k++)
            {
                int p = (1 - g[op_j][k]) * 4;
                tmp = (tmp + p * bf[k]) % mod;
                f[j] = tmp;
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= 6; i++)
        ans = (ans + f[i]) % mod;
    cout << ans << endl;
    system("pause");
    return 0;
}

f[i][j]表示:高度为i层,且最顶上的骰子数字j朝上的方案数。

一开始受到ACwing中用spfa算法求右边数限制的最短路那道题的影响,陷入了定势思维,

把dp的转移方程写成了

for (int i = 2; i <= n; i++)
    {
        memcpy(bf, f, sizeof(f));
        for (int j = 1; j <= 6; j++)
        {
            int op_j = op[j];
            int &tmp = f[j];
            for(int k = 1; k <= 6; k++)
            {
                int p = (1 - g[op_j][k]) * 4;
                tmp = (tmp + p * bf[k]) % mod;
            }
        }
    }

仔细观察就能发现,这样子会导致f[j]被多次累加更新,导致计算值远大于实际值。

(如果用的是二维数组这么写没有问题,但这道题因为省掉了一维数组,所以必须要借助一个临时变量)

 

正解:

注意正解和上面O(N)做法在思维方式上的不同:

O(N)做法考虑的是:我当前在第i层,我要怎么从第i-1层转移过来

正解矩阵乘法考虑的是:我当前在第i-1层,我要怎么转移到第i层

如果套用前一种思维模式的话,就会导致在给矩阵填系数的时候出错。

tip:线性代数都忘光啦!单位矩阵的形式:对角线全1,其他全0

正解代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;
typedef long long ll;

const int mod = 1e9 + 7;

int n, m, op[7];

struct Matrix
{
    int x, y;
    int **M;
    Matrix(int i, int j)
    {
        x = i;
        y = j;
        M = new int *[x + 1];
        for (int i = 1; i <= x; i++)
            M[i] = new int[y + 1];
    }
    Matrix *operator*(Matrix &t)
    {
        Matrix *tmp = new Matrix(6, 6);
        for (int i = 1; i <= x; i++)
            for (int j = 1; j <= t.y; j++)
            {
                int int_tmp = 0;
                for (int k = 1; k <= y; k++)
                    int_tmp = ((ll)int_tmp + (ll)M[i][k] * (ll)t.M[k][j]) % mod;
                tmp->M[i][j] = int_tmp;
            }
        return tmp;
    }
};

Matrix *quick_power(Matrix *a, int x)
{
    Matrix *tmp = new Matrix(6, 6);
    for(int i = 1; i <= 6; i++)
        for(int j = 1; j <= 6; j++)
            if(i == j)tmp->M[i][j] = 1;
            else tmp->M[i][j] = 0;
    while (x)
    {
        if (x & 1)
            tmp = (*tmp) * (*a);
        a = (*a) * (*a);
        x >>= 1;
    }
    return tmp;
}

signed main()
{
    op[1] = 4;
    op[4] = 1;
    op[2] = 5;
    op[5] = 2;
    op[3] = 6;
    op[6] = 3;
    cin >> n >> m;
    Matrix *st = new Matrix(1, 6), *mult = new Matrix(6, 6);
    For(i, 1, 6)
        For(j, 1, 6)
            mult->M[i][j] = 4;
    For(i, 1, 6) st->M[1][i] = 4;
    For(i, 1, m)
    {
        int a, b;
        cin >> a >> b;
        mult->M[a][op[b]] = 0;
        mult->M[b][op[a]] = 0;
    }
    st = (*st) * (*quick_power(mult, n - 1));
    int ans = 0;
    for(int i = 1; i <= 6; i++)
        ans = (ans + st->M[1][i]) % mod;
    cout << ans << endl;
    return 0;
}

 注意:用new或者malloc分配的数组变量不会默认初始化为0,要手动初始化。

posted @ 2023-12-06 22:14  Gold_stein  阅读(10)  评论(0编辑  收藏  举报