【数论-矩阵加速-基础】矩阵和与斐波那契数列有关的应用

6 篇文章 0 订阅
4 篇文章 0 订阅

前言

周六学了“数论知识杂谈”,重点是【矩阵加速】,做了几道相关的题目,还好感觉比较简单,都能理解,只是自己不熟悉结构体,导致板子不能特别顺利的打出来_(:з」∠)_

来总结总结

知识

一、更相减损术

一种可以求两个数的最大公因数的方法,最早源自《九章算术》

递归和循环都可以实现

其实有种改良的方法:

两数a,b一直大减小直至差和减数相等,最后的差值即gcd(a,b)

代码WA了,不知原因,改好以后上传...

类似于辗转相除法:

二、拉姆齐定理

这里简单提及,详见PPT,不是重点

三、矩阵及其运算

只有方阵才有行列式,即det(A)或|A|

没有什么好说的,直接写例题吧...orz...

(一)矩阵A*B

题目描述

输入

输出

Sample Input

2 3
1 2 3
3 2 1
2 
1 1
2 2
3 3

Sample Input

14 14
10 10

分析

有两种做法

(一)朴素算法:直接将矩阵的行列逐个相乘相加即可

(二)结构体:一种极其“骚操作”的做法(板子来源于一篇博客,自己有稍作改动)


代码:

(一)朴素版

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100+5;
ll a[MAXN+5][MAXN+5],b[MAXN+5][MAXN+5];
ll n,m,p;
ll Matrix(ll x,ll y)
{
    ll ret=0;
    for(int i=1;i<=m;i++)
        ret+=a[x][i]*b[i][y];
    return ret;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lld",&a[i][j]);
    scanf("%lld",&p);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=p;j++)
            scanf("%lld",&b[i][j]);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=p;j++)
            if(j==1)
                printf("%lld",Matrix(i,j));
            else
                printf(" %lld",Matrix(i,j));
        printf("\n");
    }   
    return 0;
}

(二)结构体版

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
struct Matrix
{
    int n,m;
    int c[MAXN+5][MAXN+5];
    Matrix(){memset(c,0,sizeof(c));}
    void read()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&c[i][j]);
    }
    Matrix operator*(const Matrix& a)
    {
        Matrix r;
        r.n=n;r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]+=c[i][k]*a.c[k][j];
        return r;
    }
    void print()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                if(j==1)
                    printf("%d",c[i][j]);
                else
                    printf(" %d",c[i][j]);
            printf("\n");
        }
    }
}A,B,C;
int main()
{
    scanf("%d%d",&A.n,&A.m);
    B.n=A.m;
    A.read();
    scanf("%d",&B.m);
    B.read();
    C=A*B;
    C.print();
    return 0;
}

(二)Fibonacci 第 n 项(矩阵加速)

题目描述

输入

输入n,m

对于100%的数据,1\leq n\leq 2*10^{9},1\leq m\leq 10^{9}+10

输出

Sample Input

5 1000

Sample Output

5


分析

设计两个矩阵:一个答案矩阵,一个单位矩阵

答案矩阵:ans[ 1,1 ] 即 ans[ f1,f2 ]

单位矩阵:C\begin{bmatrix} 0,1 & \\ 1,1& \end{bmatrix}

将单位矩阵的ans矩阵相乘,神奇的事情发生了——ans' [ f2,f1+f2 ]

由此可以得出:要求 f( n ) 即将答案矩阵初始化后乘上单位矩阵的(n-1)次方,答案就是最后的ans[1][1],中途不要忘了取模,因为数特别大

(超级神奇,不知道是那个大佬想出来的方法)


#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
    ll n,m;
    ll c[MAXN+5][MAXN+5];
    Matrix(){memset(c,0,sizeof(c));}
  
    Matrix operator*(const Matrix& a)
    {
        Matrix r;
        r.n=n;r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
        return r;
    }
};
Matrix pow(Matrix a,ll b)
{
    Matrix res;
    res.n=res.m=2;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;//初始化-单位矩阵
    for(;b;b>>=1)//b/=2;
    {
        if(b&1)//b%2!=0
            res=res*a;
        a=a*a;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&k,&mod);
    Matrix A;
    A.n=A.m=2;
    A.c[1][2]=A.c[2][1]=A.c[2][2]=1;
    //[0 1]
    //[1 1]
    A=pow(A,k-1);
      
    Matrix ans;
    ans.n=1;ans.m=2;
    ans.c[1][1]=ans.c[1][2]=1;//ans矩阵初始化:[1,1]即[f1,f2]
    ans=ans*A;
    printf("%lld\n",ans.c[1][1]%mod);
    return 0;
}
 

(三)Fibonacci前n项和(矩阵加速)

题目描述

大家都知道Fibonacci数列吧,f1=1,f2=1,f3=2,f4=3,…,fn=fn-1+fn-2,现在问题很简单,输入n和m,求{fn} 的前n项和Sn mod m。

输入

输入n,m

1\leq n\leq 2*10^{9},1\leq m\leq 10^{9}+10

输出

输出前n项和Sn mod m

Sample Input

5 1000

Sample Output

12


分析

只用在上一题的基础上变化一下ans矩阵和单位矩阵即可,即:

ans[ s1,f1,f2 ],C\begin{bmatrix} 1& 0 & 0& \\ 1& 0 & 1&\\ 1& 1 & 1& \end{bmatrix}

相乘即可得到:ans[ s2,f2,f3 ]


#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
    ll n,m;
    ll c[MAXN+5][MAXN+5];
    Matrix(){memset(c,0,sizeof(c));}
  
    Matrix operator*(Matrix a)
    {
        Matrix r;
        r.n=n;r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
        return r;
    }
};
Matrix pow(Matrix a,ll b)
{
    Matrix res;
    res.n=res.m=3;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;//初始化-单位矩阵
    for(;b;b>>=1)//b/=2;
    {
        if(b&1)//b%2!=0
            res=res*a;
        a=a*a;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&k,&mod);
    Matrix A;
    A.n=A.m=3;
    A.c[1][1]=A.c[2][1]=A.c[2][3]=A.c[3][1]=A.c[3][2]=A.c[3][3]=1;
    A=pow(A,k-2);
 
    Matrix ans;
    ans.n=1;ans.m=3;
    ans.c[1][1]=2;
    ans.c[1][2]=ans.c[1][3]=1;//ans矩阵初始化:[2,1,1]即[s1,f1,f2]
    ans=ans*A;
    printf("%lld\n",ans.c[1][1]%mod);
    return 0;
}
 

(四)TR的数列(矩阵加速)

TR是本社团的一个同学,老师很皮地拿来当题目了233

题目描述

TR非常喜欢数学,经常一个人拿出草稿纸研究奇奇怪怪的数学问题,最近,他突然对数列产生了兴趣,他找到一个数列,类似于斐波拉契,即:Tn=1*f1+2*f2+3*f3+……+n*fn    (fn为斐波拉契的第n项值)

现在TR想请你帮忙求Tn%m的值

输入

两个用空格隔开的整数n和m

1\leq n,m\leq2^{31}-1

输出

Tn mod m的值

Sample Input

5 5

Sample Output

1


分析

老套路,构造符合题意的ans矩阵和单位矩阵(构造的方式有很多种)

(感觉对于自己这种思路不开阔的小白来说,能不能顺利构造出来还说不准_(:з」∠)_)

ans[ 1,1,2,1,1 ] 即 ans[ T1,f1*1,f2*2,f1,f2 ]

目标是——ans' [ T2,f2*2,f3*3,f2,f3 ]

构造得单位矩阵:

C\begin{bmatrix} 1& 0 & 0 & 0 &0 \\ 0& 0 & 1 & 0 &0 \\ 1& 1 & 1 & 0 &0 \\ 0& 0 & 2 &0 &1 \\ 0 & 0 & 1 & 1 & 1 \end{bmatrix} (矩阵真难打orz)

乘一下,算一算结果,感受感受,得到:

ans' [ T1+f2*2,f2*2,3*(f1+f2),f1+f2 ] 即 ans'[ T2,f2*2,f3*3,f2,f3 ]

即探究:ans [ T(i) , f(i) * i , f(i+1)*(i+1) , f(i) , f(i+1) ] 怎么得到:

ans[ T(i+1) , f(i+1)*(i+1) , f(i+2)*(i+2) , f(i+1) , f(i+2) ]

而T(i+1) = T(i)  + f(i+1)*(i+1)

    f(i+1) = f(i) + f(i-1)

    f(i+2)*(i+2)=(i+2)*[ f(i+1) + f(i) ]


#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100;
ll k,mod;
struct Matrix
{
    ll n,m;
    ll c[MAXN+5][MAXN+5];
    Matrix(){memset(c,0,sizeof(c));}
   
    Matrix operator*(Matrix a)
    {
        Matrix r;
        r.n=n;r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j]%mod)%mod;
        return r;
    }
};
Matrix pow(Matrix a,ll b)
{
    Matrix res;
    res.n=res.m=5;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;//初始化-单位矩阵
    for(;b;b>>=1)//b/=2;
    {
        if(b&1)//b%2!=0
            res=res*a;
        a=a*a;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&k,&mod);
    Matrix A;
    A.n=A.m=5;
    A.c[1][1]=A.c[2][3]=A.c[3][1]=A.c[3][2]=A.c[3][3]=1;
    A.c[4][3]=2;
    A.c[4][5]=A.c[5][3]=A.c[5][4]=A.c[5][5]=1;
    A=pow(A,k-1);
  
    Matrix ans;
    ans.n=1;ans.m=5;
    ans.c[1][1]=1;
    ans.c[1][2]=1;ans.c[1][3]=2;ans.c[1][4]=ans.c[1][5]=1;
    //ans矩阵初始化:[1,1,2,1,1]即[T1,f1*1,f2*2,f1,f2]
    ans=ans*A;
    printf("%lld\n",ans.c[1][1]%mod);
    return 0;
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值