垒骰子
赌圣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;
}