P3216 [HNOI2011]数学作业 (矩阵快速幂)

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;
}

自己还是太弱,最后处理菜的要死

猜你喜欢

转载自www.cnblogs.com/lovedsr/p/9301009.html