poj 3233 Matrix Power Series (构造分块矩阵)

本文介绍了一种利用分块矩阵进行快速求和的算法,适用于处理大规模数据集的求和问题。通过构造特定的矩阵并运用矩阵乘法,可以在较短时间内计算出目标序列的求和结果。代码示例使用C++实现,展示了如何通过递归矩阵幂运算来加速计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:哆啦A梦传送门

题意:自己看。

参考博客:神犇

题解:分块矩阵:分块矩阵可以构造求和。

例如:我们可以这样构造,

S1=\begin{bmatrix} A &{\color{Red} E} \\ 0& E \end{bmatrix},S2=\begin{bmatrix} A^2 &{\color{Red} A+E} \\ 0&E \end{bmatrix},...,S(k+1)=\begin{bmatrix} A^k & {\color{Red} A^k+A^(k-1)+...+A+E}\\ 0& E \end{bmatrix}

还需注意一点的是:算完S(k+1),取出右上角矩阵分块后,还需减掉单位矩阵E。

 

代码不是我写的,我就按自己习惯改了下变量,主要是博主写的太溜了,不搬都对不起他:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long LL;
LL mod;
int n,k;

struct mat{
    LL a[70][70];
    mat(){ memset(a,0,sizeof(a));}

    mat operator * (const mat y)
    {
        mat ans;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
            ans.a[i][j]=(ans.a[i][j]+a[i][k]*y.a[k][j])%mod;
        return ans;
    }

    mat operator + (const mat y)
    {
        mat ans;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            ans.a[i][j]=(ans.a[i][j]+y.a[i][j])%mod;
        return ans;
    }

};

mat mat_pow(mat x,int k)
{
    mat ans;
    for(int i=1;i<=n;i++) ans.a[i][i]=1;

    while(k)
    {
        if(k&1) ans=ans*x;
        x=x*x;
        k>>=1;
    }
    return ans;
}

int main()
{

    while(~scanf("%d%d%lld",&n,&k,&mod))
    {

        mat p;

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++){
                scanf("%lld",&p.a[i][j]); ///左上角矩阵
                p.a[i][j]%mod;
            }
            p.a[i+n][i+n]=1; ///构造右下角单位阵
            p.a[i][i+n]=1;///构造左上角单位阵
        }

        n<<=1;
        mat item=mat_pow(p,k+1);
        n>>=1;

        mat ans;
        for(int i=1;i<=n;i++)
            for(int j=n+1;j<=n*2;j++) ///把右上角的矩阵分块取出
            ans.a[i][j-n]=item.a[i][j];

        for(int i=1;i<=n;i++){
            ans.a[i][i]--; ///减去单位矩阵
            if(ans.a[i][i]<0) ans.a[i][i]+=mod;
        }

        for(int i=1;i<=n;i++) ///输出
        {
            for(int j=1;j<=n;j++)
                printf("%lld ",ans.a[i][j]);
            puts("");
        }
    }
    return 0;
}


我的标签:做个有情怀的程序员。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值