1. 程式人生 > 其它 >矩陣快速冪優化DP

矩陣快速冪優化DP

一篇比較初步的方法總結。

矩陣快速冪優化遞推最經典的應用是快速求斐波那契數列的某一項,由於過於簡單在這裡沒有什麼必要提。由此引申出一類和上面一樣單純地優化遞推過程的題目。經典題目如 跳房子link,用 \(f_x\) 代表跳到 \(x\) 的方案數,用 \(F_x\) 表示 \(f\) 的字首和,那麼根據定義有 \(f_x=F_{x-n-1}\),而 \(F_x=F_{x-1}+f_x\),所以綜合一下就是 \(F_x=F_{x-1}+F_{x-n-1}\),於是就可以用矩陣快速冪來做了。轉移矩陣是這樣的:

\[\begin{bmatrix} F_x\\F_{x-1}\\\dots\\F_{x-n} \end{bmatrix} = \begin{bmatrix} 1&0&\dots&1\\ 1&0&\dots&0\\ \dots&\dots&\dots&\dots\\ 0&\dots&1&0 \end{bmatrix} \times \begin{bmatrix} F_{x-1}\\F_{x-2}\\\dots\\F_{x-n-1} \end{bmatrix} \]

然後就可以啦。由於 \(\forall i\in[n+1],F_i=1\)

(顯然是這樣的),所以只需要計算轉移矩陣的 \(m-n-1\) 次方即可。答案是 \(F_m\)中國象棋link 是相似的,用 \(f_x\) 代表當前位置放了的合法方案,用 \(g_x\) 代表當前位置沒放的合法方案,於是有 \(f_x=g_{x-1},g_x=f_{x-1}+g_{x-1}\)。用 \(t_x\) 代表二者之和,會發現 \(t_x=2g_{x-1}+f_{x-1}=t_{x-1}+t_{x-2}\)(傻逼才會這麼慢慢推,沒錯我就是傻逼)。邊界情況:\(t_1=2,t_2=3\),矩陣快速冪去做即可。

另外一種比較常見的就是 AC 自動機上套用矩陣快速冪。P3041link 在 AC 自動機那篇文章中已經說過了,有另一道題也很不錯。

禁忌link 同樣是在自動機上 DP,但是由於希望最大化串的價值,所以有貪心的想法是說一到串末就回到起點並統計答案,同時矩陣元素的意義應當是到達這一狀態的概率(期望可加)。統計答案上有一個非常常用的技巧是說在矩陣中新開一個地方用於儲存答案,對於可以貢獻答案的位置 \(x\) 賦值為 \(a_{x,p}=P\),然後為了方便統計答案要搞一個 \(a_{p,p}=1\),這樣就可以在答案矩陣中直接找到答案了。

還有就是圖上 DP,常見形式是什麼求走了多少步之後能到達哪些點。CF576D 是比較樸素的那種,顯然把時間分成一些階段,對於每個階段的末尾求出哪些點可以到達,然後剩下的路程考慮在當前邊集的基礎上廣搜出最短路。這裡有一個 bitset 優化乘積的方法,程式碼如下:

struct node{bitset<N>a[N];}newone;
inline node operator *(node x,node y){
	node an=newone;
	for(int i=1;i<=m;i++)for(int j=1;j<=m;j++)
		if(x.a[i][j])an.a[i]|=y.a[j];
	return an;
}

自然有許多變式,一種是常見的給邊加權,一般而言權會比較小。此時就可以考慮拆邊,把一個點拆成一串點,相鄰的點有 \(1\) 邊,新點記為 \((x,p)\)。連一條 \(x\)\(y\) 權為 \(w\) 的點可以轉化成連一條 \((x,0)\)\((y,w-1)\) 的邊即可,然後就可以正常快速冪優化了。迷路link 是板子。WYC 差不多,不用多說。