矩阵快速幂专题(一)

最近闲来无事,准备集中精力刷一波数论与图论。矩阵快速幂是数论里面的重要组成部分,值得我好好学习一下。因为题目比较多,分析也比较多,所以将此专题分成几个部分。做完这一专题,可能会暂时转向图论部分,然后等我组合数学学得差不多了,再回过头来继续做数论题。



矩阵快速幂算法的核心思想是将问题建模转化为数学模型(有一些简单题目是裸的矩阵模型,但是大部分难题就是难在要构造矩阵,用矩阵方法解决问题),推倒递推式,构造计算矩阵,用快速幂的思想求解矩阵A的n次方取mod,从而得到矩阵里面你需要的数据。

矩阵快速幂问题有某些特征,是类似快速幂的形式。首先,从题目中是一定可以得到一个递推式的,而且这个递推式不是简单的An与An-1的关系,而是关系式中带有An-2或之后的项,还有的情况甚至带有常数项和多个数列的组合(加减乘数);其次,数据量比较大,不可能用打表方法做出来;最后,有些题目可能与矩阵一点没有关系,但是通过规律分析,能够转化为已知递推式求解某一项(取模)的。

这里给出模版:  我觉得我的矩阵模版不是特别好,但是也不是特别麻烦,所以请读者自行选择是否采用

#define maxn 10+5;
typedef long long ll;
const ll mod = 1e9+7;
#define clr(x,y) memset(x,y,sizeof(x))
struct matrix 
{
	int n;
	ll maze[maxn][maxn];
	void init(int n)
	{
		this->n=n;
		clr(maze,0);
	}
	matrix operator *(const matrix& rhs)
	{
		matrix ans;
		ans.init(n);
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<n;k++)
					ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;
		return ans;
	}
};
matrix qlow(matrix a,int n)
{
	matrix ans;
	ans.init(a.n);
	for(int i=0;i<a.n;i++)ans.maze[i][i]=1;
	while(n)
	{
		if(n&1)ans=ans*a;
		a=a*a;
		n>>=1;
	}
	return ans;
}


第一题 hdu-1757 

分析:题目直接给出了递推式(带有f(x-1)到f(x-10)),而且数据量非常大需要取模(这里并不要管a0~a9的值,若不为01那么只需多考虑一步取模),这是简单的矩阵快速幂裸题。

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#define maxn 6+5
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const double pi = acos( -1 );
//const ll mod = 1e9+7;

int a[10];
ll k,m;
ll qmul(ll a,ll b)
{
     ll ans=0;
    while(b)
    {
        if(b&1)ans=(ans+a)%m;
        a=(a+a)%m;
        b>>=1;
    }
    return ans;
}
struct matrix
{
    int n;
    ll maze[maxn][maxn];
    void init(int n)
    {
        this->n=n;
        clr(maze,0);
    }
    matrix operator * (matrix & rhs)
    {
        matrix ans;
        ans.init(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                    ans.maze[i][j]=(ans.maze[i][j]+qmul(maze[i][k],rhs.maze[k][j]))%m;
        return ans;
    }
};
matrix qlow(matrix a,ll n)
{
    matrix ans;
    ans.init(a.n);
    for(int i=0;i<ans.n;i++)ans.maze[i][i]=1;
    while(n)
    {
        if(n&1)ans=ans*a;
        a=a*a;
        n>>=1;
    }
    return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    while(~scanf("%lld %lld",&k,&m))
    {
        for(int i=0;i<10;i++)
            scanf("%d",&a[i]);
        if(k<10)
        {
            printf("%lld\n",k%m);
            continue;
        }
        matrix ans;
        ans.init(10);
        for(int i=0;i<10;i++)ans.maze[0][i]=i;
        matrix ant;
        ant.init(10);
        for(int i=0;i<9;i++)ant.maze[i+1][i]=1;
        for(int i=0;i<10;i++)ant.maze[i][9]=a[9-i];
        matrix tmp;
        tmp=qlow(ant,k-9);
        ans=ans*tmp;
        printf("%lld\n",ans.maze[0][9]);
    }
    return 0;
}




第二题 hdu-1575

分析:这道题就是非常直白的矩阵快速幂,将主对角线上的数加起来取模-。-

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#define maxn 6+5
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const double pi = acos( -1 );
const ll mod = 9973;//1e9+7;

ll qmul(ll a,ll b)
{
    ll ans=0;
    while(b)
    {
        if(b&1)ans=(ans+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ans;
}
struct matrix
{
    int n;
    ll maze[maxn][maxn];
    void init(int n)
    {
        this->n=n;
        clr(maze,0);
    }
    matrix operator * (matrix& rhs)
    {
        matrix ans;
        ans.init(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                    ans.maze[i][j]=(ans.maze[i][j]+maze[i][k]*rhs.maze[k][j])%mod;
        return ans;
    }
};
matrix qlow(matrix a,int n)
{
    matrix ans;
    ans.init(a.n);
    for(int i=0;i<ans.n;i++)ans.maze[i][i]=1;
    while(n)
    {
        if(n&1)ans=(ans*a);
        a=(a*a);
        n>>=1;
    }
    return ans;
}
int main()
{
    //freopen("d:\\acm\\in.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,k;
        scanf("%d %d",&n,&k);
        matrix ans;
        ans.init(n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&ans.maze[i][j]);
        ans=qlow(ans,k);
        ll anw=0;
        for(int i=0;i<n;i++)
            anw=(anw+ans.maze[i][i])%mod;
        printf("%lld\n",anw);
    }
    return 0;
}




第三题 hdu-2604

分析:我这里只提出我的方法,其他网上大神的方法请到别的博客上查找。

对于L较大的情况(L大于2,至于为什么是2,你没有看见fmf和fff都是三个吗?),我考虑对于在E-queue里面的所有合法队列(-。-,其实和队列没有任何关系)。他们都是以mm,fm,mf,ff之一结尾的,我就分别将以他们结尾的并且总长度为n的个数记为mm[n],fm[n],mf[n],ff[n]。

考虑mm[n]后面加m是mm[n+1]的一部分,加f是mf[n+1]的一部分;

fm[n]加f属于O-queue,舍去,加m是mm[n+1]的一部分;

依次类推。。。。。。

得到递推式mm[n+1]

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值