Prefácio
(Olhe para este artigo, primeiro entenda o que é uma matriz)
A aceleração de matriz é um algoritmo muito mágico, pode resolver problemas complexos em um curto espaço de tempo com dados extremos e também é muito simples. Vamos começar com exemplos:
Exemplo 1: item de Fibonacci n
A sequência de Fibonacci satisfaz
, pergunte
. (N <= 1e9)
Esses dados são grandes e não podem ser resolvidos rotineiramente por recursão. Então, não vamos fazer um desvio, basta começar a derivação da fórmula:
Por causa de (recorrência de chave), ele pode ser colocado
em uma multiplicação de matriz (matriz n * me m * q). Por ser 2 termos por 2 termos, então m = 2, para obter uma multiplicação cumulativa regular, é necessário garantir que o resultado também seja uma matriz de n * 2, então q = 2. Como não há outros números obrigatórios, n = 1. A matriz é
, depois de multiplicada, a posição de (1,1) é
. No entanto
, a matriz da esquerda é necessária para ser
, de modo que a posição da (2,2) deve ser obtido por a "adição e multiplicação" operação de
,
e os outros dois números
. Obviamente,
então, toda a multiplicação é
, pode ser obtida
e, em seguida, multiplicada por um para
obter
, e assim por diante. Portanto
, f2 e f1 são conhecidos e uma matriz é usada para exponenciá-los rapidamente.
(Codifique a própria água)
Exemplo 2: A sequência de TR
TR gosta muito de matemática e costuma pegar rascunhos para estudar problemas matemáticos estranhos. Recentemente, ele de repente se interessou pela sequência de números. Ele encontrou uma sequência de números semelhante a Fibonacci, a saber: Tn = 1 * f1 + 2 * f2 + 3 * f3 + …… + n * fn (fn é o valor do enésimo termo de Fibonacci), agora TR gostaria de pedir sua ajuda para encontrar o valor de Tn% m (1≤n, m≤2 ^ 31 - 1).
Suponha então
Então (recursão chave), a matriz esquerda é definida como
, e então o
Então .
você entende? Finalmente , use a exponenciação rápida da matriz.
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long n,mod;
struct matrix{
int n,m;
long long c[105][105];
matrix(){memset(c,0,sizeof(c));}
matrix operator*(const matrix&a){
matrix r;r.n=n,r.m=a.m;
for(int i=1;i<=r.n;i++)
for(int j=1;j<=r.m;j++)
for(int k=1;k<=m;k++)r.c[i][j]=(r.c[i][j]+c[i][k]*a.c[k][j])%mod;
return r;
}
}a,b;
matrix mpow(matrix a,long long b){
matrix res;res.n=res.m=a.n;
for(int i=1;i<=res.n;i++)res.c[i][i]=1;
for(;b;b>>=1){
if(b&1)res=res*a;
a=a*a;
}
return res;
}
int main()
{
scanf("%lld%lld",&n,&mod);
if(n==1){
printf("%lld",1%mod);
putchar('\n');
return 0;
}
a.n=1,a.m=5;
a.c[1][1]=3,a.c[1][2]=a.c[1][4]=a.c[1][5]=1,a.c[1][3]=2;
b.n=b.m=5;
b.c[1][1]=1;
b.c[2][1]=b.c[2][3]=b.c[3][1]=b.c[3][2]=b.c[3][3]=1;
b.c[4][1]=b.c[4][3]=2,b.c[4][5]=1;
b.c[5][1]=b.c[5][3]=b.c[5][4]=b.c[5][5]=1;
a=a*mpow(b,n-2);
printf("%lld",a.c[1][1]%mod);
putchar('\n');
return 0;
}
Exemplo 3: Sequência recursiva [selecionado pela província de Shandong] (versão 2)
Resumindo
(Por exemplo, questões um e dois, apenas entenda uma solução) O método de aceleração de matriz é primeiro listar a fórmula de recorrência principal (o lado direito deve ser um termo m único) e, em seguida, listar 1 * m e m * m matrizes de acordo com a fórmula, e então é tão simples.
(update2021.3.13) Extensão: Multiplicação de Momento Generalizada
O método acima parece resolver apenas a fórmula recursiva composta por sinais de mais e menos. Na verdade, a fórmula conectada tomando os símbolos máximo e mínimo também pode ser otimizada com uma matriz. Neste momento, a multiplicação generalizada da matriz é usada:
, (Este é apenas um deles)
Pode-se provar que a multiplicação da matriz generalizada ainda satisfaz a lei associativa, mas não satisfaz a taxa de distribuição, mas isso é suficiente, pois a aceleração da matriz necessita apenas da lei associativa.
Por se livrar das restrições dos sinais de mais e menos, a aceleração da matriz pode otimizar muitos problemas aparentemente não otimizados.PD Dinâmico é um algoritmo estabelecido com base na multiplicação generalizada de momentos.