【蓝桥杯】垒骰子

垒骰子

赌圣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 0 9 10^9 109 ,数据太大了, O ( n ) O(n) O(n) 的复杂度也不行
  • 这意味着只能选比 O ( n ) O(n) O(n) 的复杂度更低的算法,那毫无疑问应该是 O ( l o g n ) O(logn) O(logn) 的算法了
  • 再找 O ( l o g n ) O(logn) O(logn) 的算法,那么就找基于二分的算法
  • 这道题看上去很复杂,也没有有序性,用不了普通的二分算法
代码如下
#include <iostream>
#define ll long long
using namespace std;

const ll mod = 1000000007;
const int oppo[7] = {0, 4, 5, 6, 1, 2, 3};

class Mtr {
   public:
    ll mt[7][7];

	//无参构造,构造全为1的矩阵
    Mtr() {
        int i = 0, j = 0;
        for (i = 1; i < 7; i++) {
            for (j = 1; j < 7; j++) {
                mt[i][j] = 1;
            }
        }
    }
    
	//有参构造,构造单位矩阵
    Mtr(int l) {
        int i = 0, j = 0;
        for (i = 1; i < 7; i++) {
            for (j = 1; j < 7; j++) {
                if (i != j)
                    mt[i][j] = 0;
                else
                    mt[i][j] = 1;
            }
        }
    }

    //计算矩阵中所有元素之和
    ll cal_all_sum() {
        ll ans = 0;
        int i = 0, j = 0;
        for (i = 1; i < 7; i++) {
            for (j = 1; j < 7; j++) {
                ans = (ans + mt[i][j]) % mod;
            }
        }
        return ans;
    }
};

//两个矩阵相乘
Mtr mtr_mul(const Mtr& m, const Mtr& n) {
    int i = 0, j = 0, k = 0;
    Mtr t;
    for (i = 1; i < 7; i++) {
        for (j = 1; j < 7; j++) {
            t.mt[i][j] = 0;
            for (k = 1; k < 7; k++) {
                t.mt[i][j] =
                    (t.mt[i][j] % mod +
                     ((m.mt[i][k] % mod * n.mt[k][j] % mod) % mod) % mod) %
                    mod;
            }
        }
    }
    return t;
}

//矩阵快速幂
Mtr cal_mtr_pow(Mtr m, ll n) {
    Mtr res(0);

    while (n) {
        if (n & 1) {
            res = mtr_mul(res, m);
        }
        m = mtr_mul(m, m);
        n >>= 1;
    }

    return res;
}

//数的快速幂
ll cal_num_pow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if ((b & 1)) res = ((res % mod) * (a % mod)) % mod;
        a = ((a % mod) * (a % mod)) % mod;
        b >>= 1;
    }
    return res;
}

void solve() {
    Mtr mtr;  //冲突矩阵
    int n, m;
    int a = 0, b = 0;
    scanf("%d%d", &n, &m);
    while (m--) {
        scanf("%d%d", &a, &b);
        //初始化冲突矩阵
        mtr.mt[oppo[a]][b] = 0;
        mtr.mt[oppo[b]][a] = 0;
    }
    //输出方法数
    printf("%lld\n",
           (cal_mtr_pow(mtr, n - 1).cal_all_sum() * cal_num_pow(4, n)) % mod);
}

int main(void) {
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木又可可

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值