矩阵快速幂入门

矩阵快速幂是通过一个状态去求出他下一个状态;

矩阵乘法的基础知识:

矩阵快速幂主要涉及的知识点:   乘法结合律: (AB)C=A(BC).

矩阵的乘法规则:

矩阵快速幂的模板(我自己常用的):

struct Matrix
{
	int M[N][N];
}A;
Matrix Mul(Matrix a,Matrix b,int NN)
{//矩阵相乘 
	int i,j,k;
	Matrix c;
	for(i = 0; i < NN; i++)
		for(j = 0; j < NN; j++)
		{
			c.M[i][j] = 0;
			for(k = 0; k < NN; k++)
				c.M[i][j] += a.M[i][k]*b.M[k][j];
		}
	return c;
}
Matrix pow(int t,int NN)
{//快速幂 
	Matrix res,a = A;
	memset(res.M,0,sizeof res.M);
	for(int i = 0; i < NN; i++)
		res.M[i][i] = 1; //单位矩阵 
	while(t)
	{
		if(t&1)
			res = Mul(res,a,NN);
		a = Mul(a,a,NN);
		t>>=1;
	}
	return res;
}

矩阵相乘时复杂度主要是在进行矩阵连乘,其复杂度为O(n^3),所以在构造矩阵是一定要注意n的取值范围;

话不多说,直接切题!

构造矩阵是至关重要的!

1.poj 3070 斐波拉契

我们都知道斐波拉契的推导式:F[n] = F[n-1]+F[n-2] (n>2);F[1] = 1,F[2] = 2;

                  \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}\begin{bmatrix} F[n-1]\\F[n-2] \end{bmatrix}= \begin{bmatrix} F[n]\\F[n-1] \end{bmatrix}

由这个推导公式我们可以构造一个A(2*2)矩阵,一个目标矩阵,通过计算A^n(次数要自己去计算)次就可以得出F[n]。

//套上模板即可 
int main()
{
    long long n,cnt = 0;
    while(~scanf("%lld",&n)&&n!=-1)
    {
        Matrix A;
        cnt = 0;
        A.M[0][0]=0;A.M[0][1]=1;
        A.M[1][0]=1;A.M[1][1]=0;
        F[0] = 1,F[1] = 1; 
        Matrix ans=pow_mod(A,n);
        cnt += F[0]*A.M[0][0]+F[1]*A.M[0][1];
    }
    return 0;
}

2.KiKi & Little Kiki2     hdu_2276

题意:给你一个环形灯 ,如果他左边的灯是开着的就在下一秒关掉该灯或者打开,问n秒后灯的状态,这样可以知道,由上一个状态去推下个状态;可以判断出该灯下一秒的状态只与它左边的一个灯相关,这样我们构造矩阵时只需够造它左边的数值以及本身,其余为0;

然后相加模2即可;

int main()
{
    while(scanf("%d",&n)==1)
    {
    	memset(T,0,sizeof T);
    	memset(A.M,0,sizeof A.M);
    	cin>>s;
    	int len = strlen(s);
    	int cnt = 0;
    	for(int i = 0; i < len; i++)
    		T[cnt++] = s[i]-'0';
    	for(int i = 0; i < cnt-1; i++)
    		A.M[i][i] =A.M[i+1][i]= 1;
		A.M[cnt-1][cnt-1] = A.M[0][cnt-1]=1;
		A = pow(n,cnt);
		for(int i = 0; i < cnt; i++)
		{
			int ans = 0;
			for(int j = 0; j < cnt; j++)
				ans+=A.M[i][j]*T[j];
			printf("%d",ans%2); 
		}
		cout<<endl;
	} 
    return 0;
}

3.hdu 5015 23333 Matrix

每次变化的都有233没次乘10加上3;

 a 0,1 = 233,a 0,2 = 2333,a 0,3 = 23333...

ai,j = a i-1,j +a i,j-1( i,j ≠ 0). Now you have known a 1,0,a 2,0,...,a n,0, 

我们应该构造

233                         10 0 0 0 1       23
x1+233                   10 1 0 0 1       x1
x1+x2+233      =     10 1 1 0 1   *   x2
x1+x2+x3+233       10 1 1 1 1       x3
3                             0  0 0 0 0       3

因而


int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(A,0,sizeof A);
        memset(C.M,0,sizeof C.M);
        for(int i = 1; i <= n; i++)
            scanf("%lld",&A[i]);
        A[0] = 23;
        A[n+1] = 3;
        for(int i = 0; i < n+1; i++)
        {
            C.M[i][0] = 10;
            C.M[i][n+1] = 1;
        }
        C.M[n+1][n+1] = 1;
        for(int i = 1; i < n+1; i ++)
            for(int j = 1; j <= i; j++)
                C.M[i][j] = 1;
        C = pow(m);
        ll ans = 0;
        for(int i = 0; i < n+2; i++)
            ans = (ans+C.M[n][i]*A[i]%mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

还有一些题目,由于时间关系,我之后整理上去!

猜你喜欢

转载自blog.csdn.net/Nothing_227/article/details/81298874