这里的操作是对所有点同时进行的。其中翻转是以坐标轴为对称轴进行翻转(两种情况),旋转则以原点为中心。如果对每个点分别进行模拟,那么m个操作总共耗时O(mn)。利用矩阵乘法可以在O(m)的时间里把所有操作合并为一个矩阵,然后每个点与该矩阵相乘即可直接得出最终该点的位置,总共耗时O(m+n)。假设初始时某个点的坐标为x和y,下面5个矩阵可以分别对其进行平移、旋转、翻转和旋转操作。预先把所有m个操作所对应的矩阵全部乘起来,再乘以(x,y,1),即可一步得出最终点的位置。
经典题目3 POJ3233 (感谢rmq)
http://blog.csdn.net/u013081425/article/details/30265311
A + A^2 + A^3 + A^4 + A^5 + A^6 =(A + A^2 + A^3) + A^3*(A + A^2 + A^3)
应用这个式子后,规模k减小了一半。我们二分求出A^3后再递归地计算A + A^2 + A^3,即可得到原问题的答案。
经典题目4 VOJ1049
http://blog.csdn.net/u013081425/article/details/38412367
经典题目6 给定n和p,求第n个Fibonacci数mod p的值,n不超过2^31
http://blog.csdn.net/u013081425/article/details/38413333
经典题目7 VOJ1067
http://blog.csdn.net/u013081425/article/details/38423731
经典题目8 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值
http://blog.csdn.net/u013081425/article/details/38433887
把给定的图转为邻接矩阵,即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即可。
题目中的数据规模保证前缀数不超过100,一次矩阵乘法是三方的,一共要乘log(n)次。因此这题总的复杂度是100^3 * log(n),AC了。
题目九的源代码,留着参考:
#include <cstdio> #define SIZE (1<<m) #define MAX_SIZE 32 using namespace std; class CMatrix { public: long element[MAX_SIZE][MAX_SIZE]; void setSize(int); void setModulo(int); CMatrix operator* (CMatrix); CMatrix power(int); private: int size; long modulo; }; void CMatrix::setSize(int a) { for (int i=0; i<a; i++) for (int j=0; j<a; j++) element[i][j]=0; size = a; } void CMatrix::setModulo(int a) { modulo = a; } CMatrix CMatrix::operator* (CMatrix param) { CMatrix product; product.setSize(size); product.setModulo(modulo); for (int i=0; i<size; i++) for (int j=0; j<size; j++) for (int k=0; k<size; k++) { product.element[i][j]+=element[i][k]*param.element[k][j]; product.element[i][j]%=modulo; } return product; } CMatrix CMatrix::power(int exp) { CMatrix tmp = (*this) * (*this); if (exp==1) return *this; else if (exp & 1) return tmp.power(exp/2) * (*this); else return tmp.power(exp/2); } int main() { const int validSet[]= {0,3,6,12,15,24,27,30}; long n, m, p; CMatrix unit; scanf("%d%d%d", &n, &m, &p); unit.setSize(SIZE); for(int i=0; i<SIZE; i++) for(int j=0; j<SIZE; j++) if( ((~i)&j) == ((~i)&(SIZE-1)) ) { bool isValid=false; for (int k=0; k<8; k++) isValid=isValid||(i&j)==validSet[k]; unit.element[i][j]=isValid; } unit.setModulo(p); printf("%d", unit.power(n).element[SIZE-1][SIZE-1] ); return 0; }