题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4965
题目大意:给出一个N*K和一个K*N的矩阵A,B(4<=N<=1000&&2<=K<=6)按顺序进行如下四种操作之后,求出最终得到的矩阵的所有元素的和。
step 1 :令C=A*B;
step 2 :令D=C^(N*N);
step 3 :D中所有元素模6
step 4 :求出D中所有元素之和
首先当然想到的只能是顺着做下来,但是想想,step 2中,我们求一个N*N的矩阵就算用矩阵快速幂,那复杂度也是O(N^3*logN),这很明显已经超时。那么应该怎么做呢?
再看看数据,K最大只有6?那我们可不可以转化一下方法,将复杂度变成K相关呢?换一下相乘的顺序吗?
突然想到这样一个式子: (AB)^N=A(BA)^(N-1)B
利用这样一个式子,我们就可以将快速幂的复杂度降到O(K^3*logK) 这样就 已经节约了大量的时间。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #define N 1010 using namespace std; int** mul(int** A,int** B,int n,int m,int l)//其实不用返回二重指针这样麻烦,先开好自己需要用的数组,每次将该数组传进来赋值给该数组即可。下同 { int **t=new int*[n]; for(int i=0;i<n;i++) t[i]=new int[l]; for(int i=0;i<n;i++) { for(int j=0;j<l;j++) { int tmp=0; for(int k=0;k<m;k++) { tmp+=(A[i][k]*B[k][j]); } t[i][j]=tmp%6; } } return t; } int** expo(int **p,int k,int n) { if(k==1) return p; int **e=new int*[n]; for(int i=0;i<n;i++) e[i]=new int[n]; for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) e[i][j] = (i == j); while(k) { if(k&1) e=mul(e,p,n,n,n); p=mul(p,p,n,n,n); k>>=1; } return e; } int main() { #ifndef ONLINE_JUDGE freopen("D:/in.txt","r",stdin); //freopen("D:/my.txt","w",stdout); #endif int NN,K; while(~scanf("%d%d",&NN,&K)&&(NN||K)) { int **a=new int*[NN]; for(int i=0;i<NN;i++) a[i]=new int[K]; int **b=new int*[K]; for(int i=0;i<NN;i++) b[i]=new int[NN]; int **c; for(int i=0;i<NN;i++) for(int j=0;j<K;j++) scanf("%d",&a[i][j]); for(int i=0;i<K;i++) for(int j=0;j<NN;j++) scanf("%d",&b[i][j]); c=mul(b,a,K,NN,K); int temp=NN*NN-1; c=expo(c,temp,K); c=mul(a,c,NN,K,K); c=mul(c,b,NN,K,NN); int ans=0; for(int i=0;i<NN;i++) for(int j=0;j<NN;j++) ans+=c[i][j]; printf("%d\n",ans); } return 0; }