1. 程式人生 > >P3216 [HNOI2011]數學作業 (矩陣快速冪)

P3216 [HNOI2011]數學作業 (矩陣快速冪)

接下來 hnoi2011 輸入 matrix spl play clu 快速冪 define

P2774 方格取數問題

題目背景
none!

題目描述
在一個有 m*n 個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意 2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數算法。對於給定的方格棋盤,按照取數要求編程找出總和最大的數。

輸入輸出格式
輸入格式:
第 1 行有 2 個正整數 m 和 n,分別表示棋盤的行數和列數。接下來的 m 行,每行有 n 個正整數,表示棋盤方格中的數。

輸出格式:
程序運行結束時,將取數的最大總和輸出

輸入輸出樣例
輸入樣例#1:
3 3
1 2 3
3 2 3
2 3 1
輸出樣例#1:
11
說明

m,n<=100

遞推式容易得到:\[ f[i+1]=f[i]*10^{k}+i+1 \]
範圍 $ n<=10^{18} $
線性算法肯定TLE,那就考慮log的算法(快速冪或者倍增)
考慮把遞推式轉換成矩陣
遞推式有三項
經驗告訴我們,也許要用到\(3*3\)的矩陣
經過一系列 碰數,湊數,計算
我們得到矩陣
\[\begin{pmatrix} f[n+1],n+1,1 \end{pmatrix}= \begin{pmatrix} f[n],n,1 \end{pmatrix} \times \begin{bmatrix} 10^{k},0,0\\1,1,0\\1,1,1 \end{bmatrix} \]


從而可以得到
\[\begin{pmatrix} f[n+1],n+1,1 \end{pmatrix}= =\begin{pmatrix} f[1],1,1 \end{pmatrix} \times \begin{bmatrix} 10^{k},0,0\\1,1,0\\1,1,1\end{bmatrix}^{n-1}\]
ps:k是位數

k雖然是不確定的,但k的範圍卻很小 <=18
所以分開做就可以了

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
ll n,mod;
struct node {
    ll m[4][4];
} ans,ss,a;
node mul(node x,node y) {
    node c= {};
    for(int i=1; i<=3; ++i)
        for(int j=1; j<=3; ++j)
            for(int k=1; k<=3; ++k)
                c.m[i][j]=(c.m[i][j]+(x.m[i][k]*y.m[k][j])%mod)%mod;
    return c;
}
void fpow(ll p) {
    while(p) {
        if(p&1) ans=mul(ans,ss);
        ss=mul(ss,ss);
        p>>=1;
    }
}
int main() {
    //全部開long long不要質疑
    cin>>n>>mod;
    ans.m[1][3]=a.m[1][1]=a.m[2][1]=a.m[2][2]=a.m[3][1]=a.m[3][2]=a.m[3][3]=1;
    for(ll i=1,j; i<=n; i=j+1) {
        j=i*10-1;
        if(j>n) j=n;
        a.m[1][1]=a.m[1][1]*(ll)10%mod;
        ss=a;
        fpow(j-i+1);
    }
    printf("%lld\n",ans.m[1][1]%mod);
    return 0;
}

自己還是太弱,最後處理菜的要死

P3216 [HNOI2011]數學作業 (矩陣快速冪)