矩阵快速幂 ACM-ICPC 2018 焦作赛区网络预赛 L. Poor God Water

引用某位大佬的语句
固定由一些项推出一些项的多维递推可以考虑矩阵快速幂。
这道题,由n-1的好多项,推出n的好多项,所以考虑矩阵快速幂。
而类似于斐波那契数列等应用也可以用矩阵快速幂
模板:

const int N=10;
int n,mod;
int temp[N][N];
int res[N][N],a[N][N];
void mul(int a[][N],int b[][N])//矩阵乘法
{
    memset(temp,0,sizeof(temp));
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
            for(int k=0;k<N;k++)
                temp[i][j]=(temp[i][j]+a[i][k]*b[k][j]%mod)%mod;
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
            a[i][j]=temp[i][j];
    return ;
}
void fun(int nn)//快速幂,只不过底数换成了矩阵
{
    memset(res,0,sizeof(res));
    for(int i=0;i<N;i++)
        res[i][i]=1;//单位阵
    while(nn){
        if(nn&1)//奇数的话res×a
            mul(res,a);
        mul(a,a);//a自己平方
        nn>>=1;//幂次/2
    }
    return ;
}
肉/鱼/巧 代表1/2/3
最后两位:(1,1)代表最后两小时吃肉。。。
0--->11,1--->12,2--->13
3--->21,4--->22,5--->23
6--->31,7--->32,8--->33
初始矩阵
111111111
000000000
000000000
000000000
000000000
000000000
000000000
000000000
000000000
意思是初始为
(1,1)个数=1
(1,2)个数=1
。。。
(3,3)个数=1
变换矩阵为
011000000
000111000
000000101
111000000
000101000
000000011
110000000
000110000
000000110
(0,0)=0:
不能由0推到1 
不能由(0,0)推到(0,0),
不能由最后两个小时吃肉+肉
变成最后三个小时吃肉+肉+肉
以此类推
所以每矩阵相乘一次,代表原来的能变化多少次

思想懂了,下面是copy的代码

#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define mod 1000000007
int k;
int dim=9;//矩阵维数
struct matirx//注意矩阵的大小
{
    ll ma[20][20];
};
matirx mutimatirx(matirx a,matirx b)//矩阵乘
{
    matirx t;
    memset(t.ma,0,sizeof(t.ma));
    for(int i=0; i<dim; i++)
    {
        for(int k=0; k<dim; k++)
        {
            for(int j=0; j<dim; j++)
            {
                t.ma[i][j]+=a.ma[i][k]*b.ma[k][j];
                t.ma[i][j]%=mod;//注意要不要取模
            }
        }
    }
    return t;
}
matirx powmatirx(matirx a,long long b)//矩阵快速幂
//返回矩阵a的b次方
{
    matirx t;
    memset(t.ma,0,sizeof(t.ma));
    for(int i=0; i<dim; i++)
        t.ma[0][i]=1;
    while(b)
    {
        if(b&1)
        {
            t=mutimatirx(t,a);
        }
        a=mutimatirx(a,a);
        b>>=1;
    }
    return t;
}
int main()
{
    //freopen("input.txt","r",stdin);
    int T;
    cin>>T;
    ll n;
    while(T--)
    {
        cin>>n;
        if(n==1)
        {
            cout<<3<<endl;
            continue;
        }
        matirx res;
        memset(res.ma,0,sizeof(res.ma));
        res.ma[0][1]=1;res.ma[0][2]=1;
		res.ma[1][3]=1;res.ma[1][4]=1;res.ma[1][5]=1;
		res.ma[2][6]=1;res.ma[2][8]=1;
		res.ma[3][0]=1;res.ma[3][1]=1;res.ma[3][2]=1;
		res.ma[4][3]=1;res.ma[4][5]=1;
		res.ma[5][7]=1;res.ma[5][8]=1;
		res.ma[6][0]=1;res.ma[6][1]=1;
		res.ma[7][3]=1;res.ma[7][4]=1;
		res.ma[8][6]=1;res.ma[8][7]=1;
        res=powmatirx(res,n-2);//求res矩阵的n次幂
        ll ans=0;
        for(int i=0;i<9;++i)
        {
            ans+=res.ma[0][i];
            ans%=mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值