蓝桥杯 垒骰子(矩阵快速幂)



垒骰子


赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。 
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 10^9 + 7 的结果。


不要小看了 atm 的骰子数量哦~


「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 数字不能紧贴在一起。


「输出格式」
一行一个数,表示答案模 10^9 + 7 的结果。


「样例输入」
2 1
1 2


「样例输出」
544


「数据范围」
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100

对于 100% 的数据:0 < n <= 10^9, m <= 36


分析:

考虑最低一层,每个面都可以朝上,此时每个面朝上的情况都是1。考虑第二层的时候,我们假设面1 和面2不能互相接触,也就是说,前一层面1朝上和当前层面5朝上的情况不能发生,同理,前一层面2朝上跟当前层面4朝上的情况也不能发生。我们用一个数组来记录这种情况。我们可以发现,每一层的数目都只与前一层的数目有关。用dp就可以解决这个题目,但还不够快,我们可以用线性代数的知识,利用矩阵快速幂进行优化。

6乘6的转移矩阵a[i][j],a[i][j]=1表示当前层以i为顶面跟前一层以j为顶面不冲突,反之则冲突。1乘6基础矩阵的b[i]表示以i为顶面的情况数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct matrix
{
    int n, m;
    ll a[7][7];
};
int fro[7] = {0, 4, 5, 6, 1, 2, 3};
matrix matrix_mul(matrix A, matrix B, int mod)
{
    matrix C;
    C.n = A.n;
    C.m = B.m;
    for(int i = 1; i <= A.n; i++)
        for(int j = 1; j <= B.m; j++)
        {
            C.a[i][j] = 0;
            for(int k = 1; k <= A.m; k++)
            {
                C.a[i][j] += (A.a[i][j]*B.a[k][j] % mod);
            }
        }
    return C;
}
matrix unit()
{
    matrix res;
    res.n = 6;
    res.m = 6;
    for(int i = 1; i <= res.n; i++)
        for(int j = 1; j <= res.m; j++)
            if(i == j)
                res.a[i][j] = 1;
            else
                res.a[i][j] = 0;
    return res;
}
matrix matrix_pow(matrix A, int n, int mod)
{
    matrix res = unit(), temp = A;
    for(; n; n /= 2)
    {
        if(n&1)
            res = matrix_mul(res, temp, mod);
        temp = matrix_mul(temp, temp, mod);
    }
    return res;
}
ll pow_mod(ll a, int n, int mod)
{
    ll res = 1, temp = a;
    for(; n; n/=2)
    {
        if(n&1)
            res = res*temp%mod;
        temp = temp*temp%mod;
    }
    return res;
}
int main()
{
    int m, u, v;
    ll ans = 0, n;
    scanf("%lld%d", &n, &m);
    matrix col;
    col.n = 6;
    col.m = 6;
    for(int i = 0; i < 7; i++)
        for(int j = 0; j < 7; j++)
            col.a[i][j] = 1;
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d", &u, &v);
        col.a[fro[u]][v] = 0;
        col.a[fro[v]][u] = 0;
    }
    matrix res, base;
    for(int i = 1; i <= 6; i++)
        base.a[1][i] = 1;
    base.n = 1;
    base.m = 6;
    int MOD = 1e9+7;
    res = matrix_pow(col, n-1, MOD);
    res = matrix_mul(base, res, MOD);
    for(int i = 1; i <= 6; i++)
        ans = (ans + res.a[1][i]) % MOD;
    ans = ans * pow_mod(4, n, MOD) % MOD;
    printf("%lld\n", ans);
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值