15年蓝桥杯第9题 矩阵快速幂

题意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\n", ans);
63     }
64     return 0;
65 }
View Code

第二个,矩阵快速幂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\n", ans_num);
116     }
117     return 0;
118 }
View Code

 

你可能感兴趣的:(15年蓝桥杯第9题 矩阵快速幂)