矩阵乘法加速图上问题专题总结
Posted explorerxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了矩阵乘法加速图上问题专题总结相关的知识,希望对你有一定的参考价值。
矩阵乘法加速图上问题专题总结
前言的前言:个人总结博客,总结知识/题目,防止很快忘掉,达到应用水平*。
前言/引子
矩阵乘法在图上问题主要应用是矩阵快速幂,一般来说的都是根据具有结合律性质的DP式子然后进行矩阵快速幂来加速。
和线段树优化建图,点分治等专题一样,代码涉及到名字的部分就变得简单,这个专题难点就在于DP的设计和矩阵的设计上,容易的就是套路性的矩阵乘法快速幂。
① 普通加速/到达图上某点方案数
例题1 [HNOI2002] 公交车路线
大概题意: 有8个呈环形的公交车站,按照A->H的排列成一圈,从A出发,第一步可以向H也可以向B(每次都是双向),问n步到达E的方案数量。
先用第一个例题来简单解释一下为什么用矩阵快速幂,怎么用矩阵快速幂。
设置DP状态\\(f[i][j][k]\\)表示从j走了i步到达k的方案数量,那么最终答案就是 f[n][1][5]。
然后有一个初始状态(邻接矩阵):
f[1]={
{0,1,0,0,0,0,0,1},
{1,0,1,0,0,0,0,0},
{0,1,0,1,0,0,0,0},
{0,0,1,0,1,0,0,0},
{0,0,0,1,0,1,0,0},
{0,0,0,0,1,0,1,0},
{0,0,0,0,0,1,0,1},
{1,0,0,0,0,0,1,0},
}
要转移到下一个状态,显然有
\\(f[2][i][j]=\\sum f[1][i][k]*f[1][k][j]\\)
如果是多次转移
\\(f[3][i][j]=\\sum f[2][i][k]*f[1][k][j]\\)
\\(f[a+b][i][j]=\\sum f[a][i][k]*f[b][k][j]\\)
多考虑它们的状态的意义,不难理解。
这个和矩阵的运算
\\(a_{i,j}=\\sum a_{i,k}*a_{k,j}\\) 是一模一样,只是去掉了第一维度的考虑。
所以我们把\\(f[1][][]\\) 自乘n遍,就能够得到最终的\\(f[n]\\) ,也就是说对于初始矩阵做\\((f[1])^n\\) 这样的运算就好,我们知道矩阵是有结合律的,所以可以做快速幂。
\\(O(8^3logn)\\) 解决问题。
例题2 [TJOI2017] 可乐
这道题多了一个建边的考虑,因为机器人不只会行走,还会原地停留,还会自爆,所以考虑对于自爆新建一个0号点,让所有点连上去,然后所有点多一个自环,其它就正常连边建出邻接矩阵跑矩阵快速幂即可。
② 点边互换(trick向/建图)/到达图上某点方案数
例题3 [SDOI2009] HH去散步
这道题规定上一个走过的边不能立刻再走,那么考虑把一条边拆成两个点,一个[i->j]一个[j->i] (真正的编号就是链式前向星建图时产生的编号),其中[i->j]算作真正到达了i点后能一步到j的情况,然后让点[i->j] 连向除了[j->i]之外所有的点[j->k],表示到达j之后再向k是有一步方案的(到 \\(i\\) 就没有),然后对于这个新的邻接矩阵跑矩阵快速幂,找到最终为[i->T]的所有点的方案数量即可,注意我们定义点的方式,[i->j]表示一步到达j的方案,也就是说要走到j还需要1步,所以快速幂要跑t-1次,表示t-1步之后的到达情况,留一步是为了[i->T]这个路可以走过去。
trick(常用/常数优化) 关于答案统计的矩阵
我们可以发现有时候我们只需要一个点S在最终所达的点的方案信息,所以统计答案的时候可以设计\\(f.H=1,f.W=n\\) ,然后设计\\(f[1][S]=k\\) 其中k表示S点位置的初始状态,然后用这个乘上后边整个转移的矩阵,这样可以降低一点常数。比如这道题,我们只需要知道从\\(f[S][i]\\)(i都是S一步能到达的点的入边)这些状态出发最终得到的答案,所以可以设计1*n的矩阵其中\\(f[1][i]\\)为1(i的含义如上述),假设我们得到了最终的答案状态,那么将这些i能够到达的j与之匹配做一次转移即可,换言之,将它与最终的矩阵做乘法(f[1][i]在左)就能够提取出以i为起点的所有答案,且答案都在T的入边上(要求得到的就是转移到这些边上的答案)。
③拆点(trick向/建图)
例题4 [SCOI2009] 迷路
带边权的图,不过边权取值为19,所以考虑把每个点拆成9个点,第1个点表示入点,从这个点出表示边权为1,其余则是29,每个点拆的9个点成一条链,然后根据边权的不同分别连边即可,最终方案累计在每个点的1号拆分点上。
④预处理多个矩阵(trick向)
例题5 [ZJOI2005]沼泽鳄鱼
每次时间过了2,3,4以及其公倍数(最大为12)时图的限制都会不一样,所以考虑处理出12个邻接矩阵,然后以12个图为一组,搞一个"12次“矩阵,对于整除12的时间做快速幂,余数做暴力乘法。
⑤矩阵乘法重定义(min/max/+/*的结合律)
例题6 [USACO07NOV] Cow Relays G
经典重定义,求最短路,考虑Floyd的转移方程\\(f[i][j]=min(f[i][k]+f[k][j])\\),这个方程式满足结合律的,而且其枚举形式也就是矩阵乘法的样子,所以对于矩阵乘法中每一步中乘法的类加改成加法的取min即可。
例题7 [NOIonline 2020 入门组] 魔法
DP设置为 \\(f[i][j][k]\\) 表示用了\\(k\\)次魔法后从 \\(i\\) 到 \\(j\\) 的最短路, 那么有\\(f[a+b][i][j]=min(f[a][i][k]+f[b][k][j])\\) ,那好了,显然满足结合律了,进行一波预处理不使用魔法和使用依次魔法的状态矩阵,然后快速幂重定义的矩阵就好。
例题8 [lgP3821]Isaac
类似沼泽鳄鱼,每次过了2,3,4,的公倍数时图的限制会不一样,然后这道题只是让求最终能否到达以及到达的路中的边中最大所需生命值最小的那条路的所需生命值,所以需要对矩阵乘法重定义,\\(f[i][j]=min\\{max(f[i][k],f[k][j]) \\}\\) ,然后进行沼泽鳄鱼同样的操作即可。
例题9 [NOIonline 2020 提高组]魔法值 (二进制拆分)
首先考虑DP统计,\\(f[i][u]=xor(e[v][u]*f[i-1][v])\\) 即每次异或的值都是边的存在值*上一次点的异或值,一般来说乘法和异或是不具有结合律的,但是这道题里每次乘法只是0/1,所以说伪结合律就存在了,转移就相当于乘了\\(k\\)次邻接矩阵,不过这道题多次询问,所以说每次都暴力很不经济,考虑预处理出一些\\(2^k\\)次的邻接矩阵,然后每次询问的时候用我们上边提到的优化常数的trick,用一个\\(1 *n\\)的矩阵来算答案,因为预处理过不需要对\\(n * n\\)的矩阵做快速幂,每次做乘法只用n^2,所以最终复杂度只要\\(n^2loga\\) 就可以了, 这样就能满足要求。
例题10 [POI2010]CHO-Hamsters (字符串转换为图)
这道题比较硬。需要一点字符串的知识,然后就没啥了(然而我字符串学的不好)。首先DP状态设置为\\(f[i][j][k]\\)表示拼接i个字符,第i次是在j后边接k的最短字符长度,那么\\(f[a+b][i][j]=min(f[a][i][k]+f[b][k][j])\\) ,显然它满足结合律,为此需要处理一个初始的状态,即每两个字符之间拼接后的长度,然后得到\\(f[1][i][j]\\)即已经接了一个字符\\(i\\)之后再接j得到的长度,这个可以用KMP来算(做时竟不会用KMP做这个事情),然后直接加速在最终矩阵取min即可(方便起见可以设计第0行来承接每次第一个选择的字符串是i的答案)。另外这道题赋初值很重要,一开始a[0][i]初值要赋值为每个len[i]。
⑥bitset优化(01矩阵/重定义)
例题11 [CF576D] Flights for Regular Customers
判联通然后判最短路,联通方程为floyd:\\(f[i][j]=f[i][k]\\&f[k][j]\\),然后每次过了d可以加一条边,也就说转移矩阵需要重新搞一个新的f数组做一遍floyd,中间转移的时候复杂度是\\(kn^3logn\\),有点高,所以考虑把所有的行状态搞一个bitset给压起来,然后转移方程转换为:\\(if(a[i][k])\\ f[i]|=f[k]\\),这样就能把每一行能够转移到的状态一次性处理出来,复杂度就变成了\\(\\frac{kn^3longn}{w}\\) 。
综合性挑战 [NOI2020] 美食家 (拆点+预处理多个矩阵/二进制拆分+重定义)
迷路+FFRC(例题11)(非01故无法bitset优化,中间过程类似)+魔法值的综合题目
引用
以上是关于矩阵乘法加速图上问题专题总结的主要内容,如果未能解决你的问题,请参考以下文章