【题意】:有n只猫咪,开始时每只猫咪有花生0颗,现有一组操作,由下面三个中的k个操作组成:
1. g i 给i只猫咪一颗花生米
2. e i 让第i只猫咪吃掉它拥有的所有花生米
3. s i j 将猫咪i与猫咪j的拥有的花生米交换
现将上述一组操作做m次后,问每只猫咪有多少颗花生?
【题解】:m达到10^9,显然不能直接算。
因为k个操作给出之后就是固定的,所以想到用矩阵,矩阵快速幂可以把时间复杂度降到O(logm)。问题转化为如何构造转置矩阵?
说下我的思路,观察以上三种操作,发现第二,三种操作比较容易处理,重点落在第一种操作上。
有一个很好的办法就是添加一个辅助,使初始矩阵变为一个n+1元组,编号为0到n,下面以3个猫为例:
定义初始矩阵A = [1 0 0 0],0号元素固定为1,1~n分别为对应的猫所拥有的花生数。
对于第一种操作g i,我们在单位矩阵基础上使Mat[0][i]变为1,例如g 1:
1 1 0 0
0 1 0 0
0 0 1 0
0 0 0 1,显然[1 0 0 0]*Mat = [1 1 0 0]
对于第二种操作e i,我们在单位矩阵基础使Mat[i][i] = 0,例如e 2:
1 0 0 0
0 1 0 0
0 0 0 0
0 0 0 1, 显然[1 2 3 4]*Mat = [1 2 0 4]
对于第三种操作s i j,我们在单位矩阵基础上使第i列与第j互换,例如s 1 2:
1 0 0 0
0 0 0 1
0 0 1 0
0 1 0 0,显然[1 2 0 4]*Mat = [1 4 0 2]
现在,对于每一个操作我们都可以得到一个转置矩阵,把k个操作的矩阵相乘我们可以得到一个新的转置矩阵T。
A * T 表示我们经过一组操作,类似我们可以得到经过m组操作的矩阵为 A * T ^ m,最终矩阵的[0][1~n]即为答案。
上述的做法比较直观,但是实现过于麻烦,因为要构造k个不同矩阵。
有没有别的方法可以直接构造转置矩阵T?答案是肯定的。
我们还是以单位矩阵为基础:
对于第一种操作g i,我们使Mat[0][i] = Mat[0][i] + 1;
对于第二种操作e i,我们使矩阵的第i列清零;
对于第三种操作s i j,我们使第i列与第j列互换。
这样实现的话,我们始终在处理一个矩阵,免去构造k个矩阵的麻烦。
至此,构造转置矩阵T就完成了,接下来只需用矩阵快速幂求出 A * T ^ m即可,还有一个注意的地方,该题需要用到long long。
具体实现可以看下面的代码。
【代码】:
1 #include "iostream"
2 #include "cstdio"
3 #include "cstring"
4 using namespace std;
5 #define ll long long
6 #define maxn 105
7 int n, m, k;
8 struct Mat {
9 ll val[maxn][maxn];
10 void zero() {
11 memset(val, 0, sizeof(val));
12 }
13 void unit() {
14 zero();
15 for(int i = 0; i < maxn; i++) val[i][i] = 1;
16 }
17 }A, T;//A = 初始矩阵 ,T = 转置矩阵
18
19 Mat operator *(const Mat &a, const Mat &b) {
20 Mat tmp;
21 tmp.zero();
22 for(int k = 0; k <= n; k++) {
23 for(int i = 0; i <= n; i++) {
24 if(a.val[i][k])
25 for(int j = 0; j <= n; j++)
26 tmp.val[i][j] += a.val[i][k] * b.val[k][j];
27 }
28 }
29 return tmp;
30 }
31
32 Mat operator ^(Mat x, int n) {
33 Mat tmp;
34 tmp.unit();
35 while(n) {
36 if(n & 1) tmp = tmp * x;
37 x = x * x;
38 n >>= 1;
39 }
40 return tmp;
41 }
42
43 void init() {
44 A.zero();
45 A.val[0][0] = 1;
46 T.unit();
47 }
48
49 int main() {
50 char s[5];
51 int a, b;
52 while(~scanf("%d%d%d", &n, &m, &k)) {
53 if(!n && !m && !k) break;
54 init();
55 for(int i = 0; i < k; i++) {
56 scanf("%s", s);
57 if(s[0] == 'g') {
58 scanf("%d", &a);
59 T.val[0][a]++;
60 } else if(s[0] == 'e') {
61 scanf("%d", &a);
62 for(int i = 0; i <= n; i++) T.val[i][a] = 0;
63 } else {
64 scanf("%d%d", &a, &b);
65 for(int i = 0; i <= n; i++) swap(T.val[i][a], T.val[i][b]);
66 }
67 }
68 Mat ans = A * (T ^ m);
69 for(int i = 1; i <= n; i++) printf("%lld ", ans.val[0][i]);
70 printf("\n");
71 }
72 return 0;
73 }