[学习笔记] 矩阵快速幂

在切TJOI2015时遇到需要矩阵加速的状压Dp,特此来学习一下。

 

入门题 poj3070 斐波那契数列

直接根据f(n) = f(n - 1) + f(n - 2) 递推 , n=0时特判一下,ans.M[1][1]就是答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
typedef long long ll;
const int Mod = 1e4,N = 10;

struct matrix{
    int M[N][N];
    int n,m;
    
    void operator * (matrix x){
        int i,j,k;
        matrix temp;
        for(i=1;i<=n;i++) for(j=1;j<=x.m;j++) temp.M[i][j]=0;
        for(i=1;i<=n;i++)
            for(j=1;j<=x.m;j++)
                for(k=1;k<=m;k++)
                    temp.M[i][j]=(temp.M[i][j]+M[i][k]*x.M[k][j])%Mod;
        for(i=1;i<=n;i++)
            for(j=1;j<=x.m;j++)
                M[i][j]=temp.M[i][j];
    }
}c,ans; 

void power(int n){
    if(n<=0) return;
    
    for(; n ;n >>= 1){
        if(n&1) ans * c;
        c * c;
    }
    
    
}

int main(){
    int n;
    ans.n=1; ans.m=2;
    c.n=2; c.m=2;
    while(scanf("%d",&n)){
        if(n==-1) break;
        c.M[1][1]=c.M[1][2]=c.M[2][1]=1; c.M[2][2]=0;
        ans.M[1][1]=1; ans.M[1][2]=0;
        if(n==0) printf("0\n");
        else {
            power(n-1);
            printf("%d\n",ans.M[1][1]);
        }
    }
    return 0;
}
Fibonacci

 

 

入门题 poj3233 矩阵集合幂(应该是这么叫吧233)

这道题要先找出递推公式,也就是S_(i+1) = S_i + A^(i+1)

然后建立这样的一个转移矩阵(  I     O  )   (  S_i      )    ( S_(i+1)  )

             (             )   x   (             )    =   (                  )

             ( A     A  )        (   A_i     )    (     A_(i+1     ) 

(论没有markdown的艰苦日子)

I代表单位矩阵(主元为1,其他为0的矩阵,因为把所有元素都错当成),O代表0矩阵(任何数乘以O都会得到一个0矩阵233,这样定义吗

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int N  = 35;

int Mod;

struct matrix{
    int M[N][N];
    int l,r;
    
    matrix operator * (matrix x){
        int i,j,k;
        matrix temp; temp.l=l; temp.r=x.r;
        for(i=1;i<=l;i++)
            for(j=1;j<=x.r;j++) temp.M[i][j]=0;
        for(i=1;i<=l;i++)
            for(j=1;j<=x.r;j++)
                for(k=1;k<=r;k++)
                    temp.M[i][j]=(temp.M[i][j]+M[i][k]*x.M[k][j]) % Mod;
        return temp;
    }
    
    void operator + (matrix x){
        int i,j;
        for(i=1;i<=l;i++) 
            for(j=1;j<=r;j++) 
                M[i][j] = (M[i][j] + x.M[i][j]) % Mod;
    }
    
}ans[3],I,O,A,C[5][5],T[5][5];

void multi(){
    int i,j,k;
    for(i=1;i<=2;i++) for(j=1;j<=2;j++) T[i][j]=O;
    for(i=1;i<=2;i++)
        for(j=1;j<=2;j++)
            for(k=1;k<=2;k++)
                T[i][j] + (C[i][k]*C[k][j]);
    for(i=1;i<=2;i++)
        for(j=1;j<=2;j++)
            C[i][j]=T[i][j];
}

void multi2(){
    int i,j,k;
    for(i=1;i<=2;i++) T[1][i]=O;
    for(j=1;j<=2;j++)
        for(k=1;k<=2;k++)
            T[1][j] + (ans[k] * C[k][j]);
    for(i=1;i<=2;i++) ans[i]=T[1][i];
}

void power(int n){
    for(; n ; n >>= 1){
        if( n & 1 ) multi2();
        multi();
    }
}

int main(){
    int i,j,n,k;
    scanf("%d%d%d",&n,&k,&Mod);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            I.M[i][j]=(i==j);
            O.M[i][j]=0;
            scanf("%d",&A.M[i][j]);
        }
    I.l=I.r=O.l=O.r=A.l=A.r=n;
    ans[2]=A; ans[1]=A;
    C[1][1]=I; C[1][2]=O; C[2][1]=A; C[2][2]=A;
    power(k-1);
    for(i=1;i<=ans[1].l;i++){
        for(j=1;j<=ans[1].r;j++) printf("%d ",ans[1].M[i][j]);
        cout<<endl;
    }
    return 0;
}
Matrix Power Series

 

入门题 HDU P2276

在转换矩阵中标出与当前行数有关的点为1,其他的点都为0,这样跑快速幂就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
const int N = 105;

struct matrix{
    int l,r;
    int M[N][N];
    
    void operator * (matrix x){
        int i,j,k;
        matrix temp;
        for(i=1;i<=l;i++) for(j=1;j<=r;j++) temp.M[i][j]=0;
        for(i=1;i<=x.l;i++)
            for(j=1;j<=r;j++)
                for(k=1;k<=l;k++)
                    temp.M[i][j]=(temp.M[i][j]+M[k][j]*x.M[i][k])%2;
        for(i=1;i<=l;i++)
            for(j=1;j<=r;j++)
                M[i][j]=temp.M[i][j];
    }
}ans,c;

void power(int n){
    for(; n ; n >>= 1){
        if( n & 1 ) ans * c;
        c * c;
    }
}

int main(){
    int i,j,n; char str[N];
    while(scanf("%d",&n)!=EOF){
        scanf("%s",str);
        int len=strlen(str);
        ans.r=1; c.l=c.r=ans.l=len; 
        for(i=1;i<=len;i++) ans.M[i][1]=str[i-1]-'0';
        for(i=1;i<=len;i++)
            for(j=1;j<=len;j++) c.M[i][j]=(j==(i-1))|(j==i);
        c.M[1][len]=1;
        /*for(i=1;i<=len;i++){
            for(j=1;j<=len;j++) printf("%d ",c.M[i][j]);
            cout<<endl;
            }
        */
        power(n);
        for(i=1;i<=len;i++) printf("%d",ans.M[1][i]);
        cout<<endl;
    }
    return 0;
}
View Code

 

 

普通题 HDU P5015

思路巧妙的一道题,因为a[i][j] = a[i-1][j] + a[i][j-1],所以每一列都有一个先导,因此不能直接对a[][]进行直接矩阵快速幂,而是根据行列情况递推,然后再把题目给我们的原数列乘回来,就得出我们想要的答案,其实就是递推求出a[n][m],貌似网上还有组合数学做法,玄学

posted @ 2017-12-09 07:48  FranceDisco  阅读(152)  评论(0编辑  收藏  举报