矩阵快速幂优化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 差不多,不用多说。

posted @ 2022-12-01 11:10  Feynn  阅读(62)  评论(0编辑  收藏  举报