题意and数据范围:
赌圣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
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 2ms
第一个代码是dp,只是单纯的想用。超时超内存。
1 /* 2 思路:开始是一点思路都没有的,题都没明白,后来发现dp[]是可以做到的。因为要分类,应该是二维dp。dp[n][m] = sum(dp[n-1][x]) {m是指第n个方块朝上的数字 3 ,x是指相对应的第n-1个方块,可以朝上的数字的状态和}。初始状态,dp[1][x] = 4; {1<=x<=6} 【当然这些思考过程是和大腿一起完成的、2333】 4 当然我已经假设n是<10000的了。╭(╯^╰)╮ 5 */ 6 7 #include <stdio.h> 8 #include <string.h> 9 #include <iostream> 10 #define maxn 10000 11 #include <map> 12 #define inf 1000000007 13 using namespace std; 14 15 long long int dp[maxn][10]; 16 int mp[maxn][maxn]; 17 map<int, int> mpp; 18 19 int main() { 20 int n, m, a, b; 21 while(~scanf("%d%d", &n, &m)) { 22 memset(mp, 0, sizeof(mp)); 23 memset(dp, 0, sizeof(dp)); 24 mpp.clear(); 25 mpp[1] = 4; 26 mpp[2] = 5; 27 mpp[3] = 6; 28 mpp[4] = 1; 29 mpp[5] = 2; 30 mpp[6] = 3; 31 32 for (int i=0; i<m; ++i) { 33 scanf("%d%d", &a, &b); 34 mp[a][b] = 1; 35 mp[b][a] = 1; 36 } 37 for (int i=1; i<=6; ++i) { // 初始化 38 dp[1][i] = 1; // 设为1 最后乘以4^n 39 } 40 41 for (int i=2; i<=n; ++i) { 42 for (int j=1; j<=6; ++j) { 43 for (int k=1; k<=6; ++k) { 44 int bot = mpp[j]; 45 if (mp[bot][k] == 0) { 46 dp[i][j] += dp[i-1][k]; 47 // cout << "i=" << i << " " << "j==" << j << " " << "k==" << k << endl; 48 } 49 } 50 } 51 } 52 53 long long ans = 0; 54 for (int i=1; i<=6; ++i) { 55 ans += dp[n][i]; 56 } 57 58 for (int i=1; i<=n; ++i) { 59 ans *= 4; 60 ans %= inf; 61 } 62 printf("%lld ", ans); 63 } 64 return 0; 65 }
第二个,矩阵快速幂right:
太久不写矩阵快速幂了,从理解推公式到写完大概有三个多点了。T_T。最后发现bug是A1是单位矩阵!!!对啊!单位矩阵啊!只有A1[i][i] = 0的。然而我好像想成了所有的数都是1的了.......
1 /* 2 上一个dp的方法,因为n的范围,数组没法开那么大。讲道理,求4^n的时候。没有用快速幂也是一定会超时的。 3 但是矩阵快速幂是perfect的、 4 思路:首先我们考虑的依然是递推公式。对于一个六阶矩阵,An 第a行第b列的数字 代表第一层底层数字是a,第n层底层数字是b时的排放方式。 5 然后A1 是一个单位矩阵。构造一个六阶矩阵X,第a行第b列代表相邻的两个格子底面是a 和 b时,能否成功连接。如果能,值设为1,否则设为0. 6 【关于X矩阵还是不太懂的,推理的话就是矩阵乘法的知识,An的每个状态是怎么由An-1得到的。第n层底面数字是1的时候应该对应第n-1层底面数字是 7 1~6的时候。是否稳定。所以X矩阵确实应该是这样的。】 8 (如果把An矩阵改成只有第一行的b列是代表的第n层的底层数字的话。An = X * An-1。最后依然是An = X^(n-1)。) 9 然后可以知道An = An-1 * X。A1是一个单位矩阵。所以呢。最后就是An = X^(n-1)。 10 好了。问题就是代码实现了。T_T 11 啊、、、对。注意。我们的An数组里存的是第n层的格子底层数字,所以判断是不是能和下一层稳定存在的时候要改回顶上的数字。 12 这个就是在X矩阵的时候。 13 */ 14 15 #include <stdio.h> 16 #include <string.h> 17 #include <iostream> 18 #include <map> 19 using namespace std; 20 #define inf 1000000007 21 22 struct Mat{ 23 long long m[10][10]; 24 Mat() { 25 memset(m, 0, sizeof(m)); 26 } 27 }; 28 29 long long Num_pow(long long a, int n) { // 求a*n快速幂 30 long long ans = 1; 31 while (n > 0) { 32 if (n % 2 == 1) { 33 ans *= a; 34 } 35 a *= a; 36 n >>= 1; 37 } 38 return ans; 39 } 40 41 42 Mat Matrix_mul(Mat a, Mat b) { // 矩阵乘法 43 Mat ans; 44 for (int i=1; i<=6; ++i) { 45 for (int j=1; j<=6; ++j) { 46 for (int k=1; k<=6; ++k) { 47 ans.m[i][j] += a.m[i][k] * b.m[k][j]; 48 ans.m[i][j] %= inf; 49 } 50 } 51 } 52 return ans; 53 } 54 55 Mat Matrix_pow(Mat a, int n) { //矩阵快速幂的实现 56 Mat ans; 57 for (int i=0; i<10; ++i) { 58 ans.m[i][i] = 1; 59 } 60 while(n > 0) { 61 if (n % 2 == 1) { 62 ans = Matrix_mul(a, ans); 63 } 64 a= Matrix_mul(a, a); 65 n >>= 1; 66 } 67 return ans; 68 } 69 70 71 int mp[10][10]; 72 map<int, int> mapp; 73 74 int main() { 75 int n, m; 76 int a, b; 77 while(~scanf("%d%d", &n, &m)) { 78 mapp.clear(); 79 memset(mp, 0, sizeof(mp)); 80 for (int i=0; i<m; ++i) { 81 scanf("%d%d", &a, &b); 82 mp[a][b] = 1; 83 mp[b][a] = 1; 84 } 85 mapp[1] = 4; 86 mapp[2] = 5; 87 mapp[3] = 6; 88 mapp[4] = 1; 89 mapp[5] = 2; 90 mapp[6] = 3; 91 92 Mat ans; 93 Mat x; 94 for (int i=1; i<=6; ++i) { 95 for (int j=1; j<=6; ++j) { 96 int temp = mapp[j]; 97 if (mp[i][temp] == 0) 98 x.m[i][j] = 1; // 能稳定连接 99 else x.m[i][j] = 0; 100 } 101 } 102 103 ans = Matrix_pow(x, n-1); 104 long long num = Num_pow(4, n); 105 long long ans_num = 0; 106 for (int i=1; i<=6; ++i) { 107 for (int j=1; j<=6; ++j) { 108 ans_num += ans.m[i][j]; 109 ans_num %= inf; 110 } 111 } 112 num %= inf; 113 ans_num *= num; 114 ans_num %= inf; 115 printf("%lld ", ans_num); 116 } 117 return 0; 118 }