1。 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。
--摘自,http://www.matrix67.com/blog/archives/276/
理解:
为什么k步的路径数,我们只需要二分求出A^k?
因为每步实际做一次矩阵乘法,矩阵乘法定义就是C(i,j)=ΣA(i,k)*A(k,j)。
2.经典题目9 用1 x 2的多米诺骨牌填满M x N的矩形有多少种方案,M<=5,N<2^31,输出答案mod p的结果
View Code
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> #include<vector> #include<string> #include<math.h> #include<map> #include<set> #include<algorithm> using namespace std; #define MAXN 258 #define MOD 9937 #define size (1<<M) int N, M; int ans[1200]; class Matrix { public: int mt[MAXN][MAXN]; Matrix Multiply(Matrix); Matrix Add(Matrix); Matrix quickpower(int); }AA; Matrix Matrix::Add(Matrix A) { Matrix C; for( int i = 1; i <= size; i++) for( int j = 1; j <= size; j++) C.mt[i][j] = mt[i][j] + A.mt[i][j]; return C; } Matrix Matrix::Multiply(Matrix A) { Matrix C; for( int i = 0; i < size; i++) { for( int j = 0; j < size; j++) { int sum = 0; for( int k = 0; k < size; k++) { if( A.mt[i][k] && mt[k][j] ) { sum += (A.mt[i][k] % MOD) * (mt[k][j] % MOD); sum %= MOD; } } C.mt[i][j] = sum; } } return C; } Matrix Matrix::quickpower(int n) { Matrix B; B = *this; while( n > 0 ) { if( n & 1 ) *this = (*this).Multiply(B); n = n / 2; B = B.Multiply(B); } return *this; } //横的摆放状态 void pre(int x, int state) { if( x > M ) return; if( x == M ) { ans[state] = 1; return; } pre(x + 1, state << 1 ); pre(x + 2, 3 | (state << 2) ); } int main( ) { while( scanf("%d%d",&N,&M) != EOF ) { if( N < M ) swap(N,M); memset(ans, 0, sizeof(ans)); pre(0, 0); if( N % 2 == 1 && M % 2 == 1 ) { puts("0"); continue; } for( int i = 0; i < (1<<M); i++) { for( int j = 0; j < (1<<M); j++) { AA.mt[i][j] = 0; if( ((~i)&j) == ((~i)&((1<<M)-1)) ) { AA.mt[i][j] = ans[i&j]; } } } AA = AA.quickpower(N-1); printf("%d\n",AA.mt[(1<<M)-1][(1<<M)-1]); } return 0; }