関連ブログ:https://blog.csdn.net/china_xyc/article/details/89819376#commentBox
行列の乗算は、対象DP上で最適化することができ、我々は、次の要件があります。
- だけ加算転送タイプ、等、減算をクリアし、最大と最小のに許可されていません
- 最初の数DP結果の伝達係数は一定でなければならない場合には
- 一般スーパーマルチにおける遷移の数
- 転送回数ので、一般的にINTの範囲内の弾性率を有します
要約すると、例:
DP [I] = A×DP [I-1] + DP×B [I-2] + C×DP [I-3]
ここで、A、B、Cの定数、及びDPの必要な行列の最適化、多くの場合、I 2 ^ 128等、特に鬼畜特に多数あり、
最適化された行列乗算は、DPを見つけるために[i]はOでありますタイムログ(i)が完成されます。
だから、どのように行列の乗算を達成するために、その原理はシェーンのですか?
行列乗算は、2つの行列AとBを必要とし、Aは、P×Nであり、Bは、以下に、M×Pの大きさであります
説明を容易にするために、我々は、フィボナッチ行為の例を与えます。
フィボナッチ伝達式は、DP [I] = DP [ I-1] + DP [I-2]。
我々次いで(DP [I]、DP [ I-1]) の1×2行列Aであるとみなす
行列Aを乗じた各時間はF .:転送することと等価である
|を。1. 1 |
| 0.1 |
結果である:(DP [I] + DP [I-1]、DP [i])と、 (DP [I + 1]であり、 DP [i])と
オリジナルながら一度8回必要な行列乗算演算、状態遷移が一度だけ必要なので、廃木材、それに行列乗算アルゴリズムが表示されません。。
鍵です!行列の乗算は、関連性を持っている、ちょっとちょっとちょっと、我々はすぐにパワーアップを開始することができます!それを見て、このようにO(n)のアルゴリズムの最適化は、我々は、これが最適化された使用することができますnはフライドチキンフライドチキン大きな変態時に簡単なO(8×LOGN)アルゴリズムとなっています。
あなた自身の例が行う https://www.luogu.org/problem/P5343
使用される知識:交差(ビット集合<N> =、および&)線形再帰(DP)のセット マトリックス加速度(マトリックスフラッシュパワー)(実際には、幾分スクロール・アレイのような)
線形再帰的な感じがある:F(N)= F(N-1)+ F (N-2)、 及び、値Fを知る(1)、F(2 ) 次に、Fの導入(3)の値、およびばなら再帰ダウン、それが行く上記の値を知っていることです私たちは、後者の値を知っています。
#include <cstdioを> する#include <CStringの> する#include <iostreamの> する#include <ビットセット> の#include <アルゴリズム> 使用して 名前空間STDを、 typedefの長い 長いLL。 const int型 MAXN = 105 ; const int型 MOD = 1E9 + 7 。 LLのn; int型M、X。 ビットセット <MAXN> A、B; LL G [MAXN] [MAXN]、TMP [MAXN] [MAXN]、RES [MAXN] [MAXN]。 【MAXN] DP LL。 ボイド{([] [MAXN]、LL B [] [MAXN] LL)MULT のmemset(TMP、0、はsizeof(TMP))。 以下のために(int型 i = 1 ; iは= < 100 ; iは++ ){ ため(int型の J = 1 ; J <= 100 ; J ++ ){ ための(int型のk = 1 ; K <= 100 ; ++ k個){ TMP [I] [ J] =(TMP [I] [J] + [I] [K] * B [k]は[J]%のMOD)%MOD。 } } } のために(int型 i = 1 ; iは= < 100 ; iは++ ){ ため(INTJ = 1 ; J <= 100 ; J ++ ){ [I] [J] = TMP [I] [J]。 } } } ボイドqpow {([] [MAXN]、LL N LL) のmemset(RES、0、はsizeof (RES))。 以下のために(int型 i = 1 ; iは= < 100 ; iは++ ){ RES [i]は[I] = 1 。 } 一方、(N){ 場合(N - 1 )MULT(RES、A)。 MULT(A)。 N = >> 1 。 } 以下のために(int型私= 1 ; iが= < 100 ; iが++ ){ ため(INT J = 1 ; J <= 100 ; J ++ ){ [I] [J] =のRES [I] [J]。 } } } int型のmain(){ scanf関数(" %のLLD%dの"、&N、&M)。 以下のために(int型私= 0 ; iが<M; iが++ ){ scanf関数(" %のD "、&x)は、 [X] = 1。 } のscanf(" %dの"、&M)。 以下のために(int型私= 0 ; iが<M; iが++ ){ scanf関数(" %のD "、&x)は、 B [X] = 1 。 } A&= B。 以下のために(int型 i = 1 ; iは= < 100 ; iは++ ){ 場合([i])とG [ 1 ]、[I] = 1 。 } のために(int型 I = 2 ; I <= 100; 私は++ ){ G [i]は[I - 1 ] = 1 。 } // DP [0] = 1。 // ため(INT i = 0; iが<= 100; I ++){ // ため(INT J = 1; J <= I; J ++){ // もし([J]){ // DP [I] =(DP [I] + DP [IJ])%MOD。 // } // } // } // 場合(N <= 99)のprintf( "%Dを\ n"、DP [N])。 // 他{ // qpow(G、N-99)。 // LL ANS = 0; // ため(INT i = 1; iは= 100 <; iは++){ // = ANS(ANS + DP [I-100] * G [I]%MOD [1])MOD%; // } // のprintf( "%LLDの\のN-"、ANS); // } // 元の直接マトリックスF(1)= 0であるため、高速電力ああ..、最初の直接作ることができる qpow(G、N- 0 ); (printfの" %LLDの\のN- " [Gを1 ] [ 1 ]) ; }