1. 程式人生 > >探索(數學)(矩陣快速冪)(快速乘)

探索(數學)(矩陣快速冪)(快速乘)

lld over long 技術分享 好想 二維 name i++ ans

技術分享圖片
技術分享圖片
技術分享圖片

一句話題意:三維空間劃分四維空間,最多能劃分成多少個部分。

我們直接想四維的不好想,但是一般這種題我們考慮從低維開始做起。

在經過手算之後我們可以發現:

\(f(x)\)為零維(點)切一維(線)最多劃分的部分,遞推式:\(f(x)=f(x-1)+1\)

\(g(x)\)為一維(線)切二維(平面)最多劃分的部分,遞推式:\(g(x)=g(x-1)+f(x-1)\)

\(k(x)\)為二維(平面)切三維(空間)最多劃分的部分,遞推式:\(k(x)=k(x-1)+g(x-1)\)

\(h(x)\)為三維(空間)切四維最多劃分的部分,遞推式:\(h(x)=h(x-1)+k(x-1)\)

那麽我們很容易看出這是一個遞推。但是因為數據範圍很大,所以我們考慮構造矩陣進行矩陣快速冪加速運算。(不會打mathjax矩陣,只能湊合一下了)

1 1 0 0 0
0 1 1 0 0
0 0 1 1 0
0 0 0 1 1
0 0 0 0 1

然後初始矩陣[1,f(0),g(0),k(0),h(0)]

但是因為數據實在很大,矩陣快速冪進行乘的時候很可能炸long long,所以我們用快速乘來保證數在mod範圍內。

代碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 40
using namespace std;
long long n,m;
long long f[MAXN];
struct Node{long long t[MAXN][MAXN];};
Node init;
inline long long multi(long long x,long long y,long long mod)
{
    long long ans=0;
    while(y) 
    {
        if(y&1) ans=(ans+x)%mod;
        x=(x+x)%mod;
        y>>=1;
    }
    return ans%mod;
}
inline Node mul(Node x,Node y)
{
    Node cur;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)
            cur.t[i][j]=0;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)
            for(int k=1;k<=5;k++)
                cur.t[i][j]=(cur.t[i][j]+multi(x.t[i][k],y.t[k][j],m))%m;
    return cur;
}
inline void solve()
{
    long long cur[MAXN];
    memset(cur,0,sizeof(cur));
    for(int j=1;j<=5;j++)
        for(int k=1;k<=5;k++)
            cur[j]=(cur[j]+multi(f[k],init.t[k][j],m))%m;
    for(int i=1;i<=5;i++)
        f[i]=cur[i];
}
int main()
{
    freopen("discover.in","r",stdin);
    freopen("discover.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    f[1]=1;
    f[2]=1;
    f[3]=1;
    f[4]=1;
    f[5]=1;
    for(int i=1;i<=5;i++) init.t[i][i]=1;
    init.t[1][2]=1;
    init.t[2][3]=1;
    init.t[3][4]=1;
    init.t[4][5]=1;
    while(n)
    {
        if(n&1) solve();
        init=mul(init,init);
        n>>=1;
    }
    printf("%lld\n",f[5]%m);
    return 0;
}

探索(數學)(矩陣快速冪)(快速乘)