http://poj.org/problem?id=3735
题意:
n只猫,三种命令:
1、第i只猫吃掉所有花生;
2、第i只猫得到一个花生;
3、交换第i,j只猫的花生;
先由k个 这些命令组成一个操作序列
然后重复操作序列m次,
n,k<=100,m<=1e9
m的次数那么大,可以用构造矩阵,然后用快速幂的方法
引用大神http://blog.csdn.net/magicnumber/article/details/6217602的图片
初始我们开一个1行n+1列的矩阵存放每只猫的初始值 第n+1行的前n个元素表示每只猫的花生数
org=【
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
】
在n+1阶单位矩阵的基础上,
给第1 只猫加1个花生的操作矩阵G为:
1 0 0 0
0 1 0 0
0 0 1 0
1 0 0 1 第n+1行的第i列加1
让第2只猫吃完所有花生的操作矩阵E为:
1 0 0 0
0 0 0 0
0 0 1 0
0 0 0 1 第i列清空为0
交换第1 第2 只猫的操作矩阵S为:
0 1 0 0
1 0 0 0 交换第i第j列元素
0 0 1 0
0 0 0 1
********************************************************
我们可以发现,让org初始矩阵左乘一个G矩阵,得到
【
1 0 0 0
0 1 0 0
0 0 1 0
(x+1) Y Z 1
】
恰好就是X加了1
我们可以发现,让org初始矩阵左乘一个E矩阵,得到
【
1 0 0 0
0 1 0 0
0 0 1 0
X 0 Z 1
】
恰好就是吃光了Y
我们可以发现,让org初始矩阵左乘一个S矩阵,得到
【
1 0 0 0
0 1 0 0
0 0 1 0
y x Z 1
】
恰好就是交换了Y和X的花生
******************************************************
那么对于k^m次操作,我们先处理好前k次的操作
也就是用org初始矩阵不断乘G/E/S矩阵 得到前k次的结果 (PS:由于我们可以直接知道k次每次乘了操作矩阵后的结果(交换两列、清空行,指定位置加1),就可以不必模拟k次,直接得到k次的结果了 )
接下来就是对k次得到结果矩阵X 连续左乘m次。。。这里就用个快速幂就OK,然而每次乘法O(N^3),log(m)≈26 所以还需要优化一下乘法部分,
因为显然大部分元素为0,是稀疏矩阵,我们可以用系数矩阵乘法优化
一般矩阵乘法:
Matrix mul(Matrix a, Matrix b) //矩阵相乘 { Matrix res; for(int i = 0; i < k; i++) for(int j = 0; j < k; j++) { res.mat[i][j] = 0; for(int t = 0; t < k; t++) { res.mat[i][j] += a.mat[i][t] * b.mat[t][j]; res.mat[i][j] %= mod; } } return res; }
matrix mul(matrix a,matrix b) { matrix res; memset(res.m,0,sizeof(res.m)); __int64 i,j,k; for (i=1;i<=n+1;i++) { for (j=1;j<=n+1;j++) { if (a.m[i][j]) //稀疏矩阵乘法(非O(n^3)), 即知道答案为零直接跳过 for (k=1;k<=n+1;k++) { res.m[i][k]+=a.m[i][j]*b.m[j][k]; } } } return res; }
从而解决问题,
难点是稀疏矩阵优化/构造操作矩阵
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <queue> #include <map> #include <set> #include <vector> using namespace std; struct matrix { __int64 m[105][105]; }; matrix unit_org; matrix unit; __int64 n,k; int m; void init() { char op; __int64 num,num1,num2,i,j; for (j=1;j<=k;j++) { scanf("%c",&op); if (op=='g') { scanf("%I64d",&num); getchar(); unit.m[n+1][num]++; //第num个+1 } if (op=='e') { scanf("%I64d",&num); getchar(); for (i=1;i<=n+1;i++) unit.m[i][num]=0; //第num列置为零 } if (op=='s') { scanf("%I64d%I64d",&num1,&num2); getchar(); if (num1==num2) continue; for (i=1;i<=n+1;i++) //交换两列 { num=unit.m[i][num1]; unit.m[i][num1]=unit.m[i][num2]; unit.m[i][num2]=num; } } } } matrix mul(matrix a,matrix b) { matrix res; memset(res.m,0,sizeof(res.m)); __int64 i,j,k; for (i=1;i<=n+1;i++) { for (j=1;j<=n+1;j++) { if (a.m[i][j]) //稀疏矩阵乘法(非O(n^3)), 即知道答案为零直接跳过 for (k=1;k<=n+1;k++) { res.m[i][k]+=a.m[i][j]*b.m[j][k]; } } } return res; } matrix pow_mi(matrix a,int b) { matrix res=unit_org; while(b) { if (b&1) res=mul(res,a); a=mul(a,a); b>>=1; } return res; } int main() { __int64 i,j; while(scanf("%I64d%d%I64d",&n,&m,&k)!=EOF) { getchar(); if (!n&&!m&&!k) break; memset(unit.m,0,sizeof(unit.m)); for (i=1;i<=101;i++) unit.m[i][i]=1; unit_org=unit; init(); matrix ans= pow_mi(unit,m); for (i=1;i<=n;i++) { if (i!=1) printf(" "); printf("%I64d",ans.m[n+1][i]); } printf("\n"); } return 0; }