矩阵快速幂(模板+例题)


模板

#include
#include//pow函数,其实没啥用 
using namespace std;

int n;long long k;
const int N=pow(10,9)+7;
struct node{long long  a[105][105];};
node shu,ans,mp;
//shu是输入的矩阵,ans是所求答案
node matrix(node x,node y){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            mp.a[i][j]=0;
            for(int p=1;p<=n;p++)
                mp.a[i][j]=(mp.a[i][j]+x.a[i][p] * y.a[p][j])%N;
            //矩阵乘法 
        }
    return mp;
}

int work(long long k){//矩阵快速幂 
    while(k){
        if(k&1) 
            ans=matrix(ans,shu);
        k>>=1;
        shu=matrix(shu,shu);
    }
}

int main(){
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++){ 
        for(int j=1;j<=n;j++)
            scanf("%d",&shu.a[i][j]);
        ans.a[i][i]=1;//任何一个矩阵乘以单位矩阵,其值等于本身;
    }       

    work(k);

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            printf("%d ",ans.a[i][j]);
        printf("\n");
    }
    return 0;   
}

HDU2842(不水)

英文版原题

题目描述:一根木棒上有n个环(n<=10^9) 第一个环可以随意取下或者放上 如果前k个环都不在棒子上,且第k+1个环在棒子上,则你可以取下或放上第k+2个环 给出n,求最少需要多少步可以取完棒子上的环?

思路:

1.设f[n]数组表示取下n个环所需最小次数;
2. 若想让第n个环被取下,那么前(n-2)个都要被取下,第(n-1)要挂在环上;这时所需次数为f[n-2]+1;
3. 考虑第(n-1)个环还未取下;而取下第(n-1)个环需要第(n-2)个环挂上,取下第(n-2)个环需要第(n-3)个环挂上…即先要把前(n-2个都取下来);还有f[n-1]它自己;以此类推,取下第(n-1)个环需要次数为f[n-2]+f[n-1];
4. 合并得到递推公式为:f[n] = f[n-1] + 2 * f[n-2] + 1;

题解

坐标系

先挖个坑,有些还没有搞懂

【题目描述】
小澳在坐标系的原点,他可以向上、向左或者向右走。他可以走 n 步,但不能经过相同的点。小澳想知道他有多少种走法。

【题解的想法】:
考虑合法路径的特点,如果第 i-1 步向上走,那么第 i 步可以向上、左、右走;如果第 i-1 步向左走,那么第 i 步可以向上或者向左走;如果第 i-1 步向右走,那么第 i 步可以向上或者向右走。

用 f[i][0]表示走了 i 步,第 i 步向上走的方案数;f[i][1]表示走了 i 步,第 i 步向左走的方案数;f[i][2]表示走了 i 步,第 i 步向右走的方案数,递推方程:
f[i][0]=f[i-1][0]+f[i-1][1]+f[i-1][2];
f[i][1]=f[i-1][0]+f[i-1][1];
f[i][2]=f[i-1][0]+f[i-1][2];
进行矩阵乘法优化,可以将时间复杂度降低到 O(logn)。

#include
#include
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;


struct matrix{
    ll x[2][2];
    matrix(){
        memset(x,0,sizeof(x));//赋0的有效方式 
    }
}ans,a;

matrix operator*(matrix a,matrix b){
    matrix res;
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
                res.x[i][j]=(res.x[i][j]+a.x[i][k]*b.x[k][j])%mod;
    }
    return res;
}

int main(){
    int n;scanf("%d",&n);n++;
    ans.x[0][0]=1;
    a.x[0][0]=a.x[0][1]=a.x[1][1]=1;
    a.x[1][0]=2;
    for(int i=n;i;i>>=1,a=a*a)
        if(i&1) ans=ans*a;//重载过运算符后要按照规定的用法使用该符号 
    printf("%lld\n",ans.x[0][0]);
    return 0;
}

你可能感兴趣的:(数论)