Loading

qbxt 刷题班动态规划笔记

状压

炮兵阵地

solution

老题了,状压每个行的状态,枚举前两行转移就好了。

[清华集训2012]串珠子

solution

\(dp\) 状态很显然,就是设 \(f[s]\) 表示点集 \(s\) 内所有点联通的方案数。

怎么求联通的方案数?

统计一个集合中所有点联通的方案数很难,但是可以用所有的连边情况 - 不连通的方案数的方案数就可以求出来了。

所有的连边情况:\(g[s] = \prod (c_{i,j} + 1)(i, j\in s)\) (两点之间可以不选边,所以要+1)

求不联通的的方案数?真的妙 = =

一个错误思路:枚举一个联通块 \(t\),然后剩下的点可联通可不连通,这样就保证了这个联通块和其余点之间没有边,达到了总的图不连通。方案数为: \(f[t]\times g[s - t]\)​ ,然后把枚举的答案相加就好。

这样可能会算重,因为枚举完一个联通块的时候计算一个方案,枚举另一个连通块的时候有可能会出现相同的情况,也会被记录答案。

枚举一个点所在的连通块,确定一个点 \(p\)​ ,对于每种不同的方案,\(p\)​ 点只可能在一个联通块内,枚举这个联通块 \(T\)​ ,那么不连通的方案就是 \(\sum f[T]\times g[s - T]\)

转移式子就有了:\(f[s] = g[s] - \sum f[T] \times g[s - T]\)

[P1777] 帮助

solution

很好的一道 \(dp\) 题,涉及到状压和序列。

拿书和放书是独立的。

放的时候很简单,就是找高度相同的把它们放在一起,如果没有相同就放在两端就好了。

考虑怎么拿,使得拿完之后段数最少。

\(f_{i,j,x}\) 表示考虑到第 \(i\) 本数,拿了 \(j\) 本,上一本留下来的书的高度为 \(x\)​ 的最少段数,转移枚举当前这本书拿不拿就好了。

发现这样设状态的话,放书产生的贡献就没法计算了,改一下状态。

\(f_{i,j,x,s}\)\(s\) 表示剩下的书的高度的集合,那么产生的贡献就是 \(n\) 本书的高度集合 xor 剩下的书的高度集合,就是放书产生的贡献。

[省选联考 2020 A/B 卷]信号传递

solution

读题可知:令 \(i < j\)​ ,如果从 i -> j 代价就是 \(j - i\)​ ,如果从 \(j\)​ 传到 \(i\)​ ,那么代价就是 \(k(i + j)\)​ 。

\(O(n)\) 预处理一个 \(c_{i, j}\) 表示 i->j 信号传递了多少次,

最后的答案就是求所有的 \(c_{i, j}\times (j - i) + c_{j,i}\times(i + j)k\)

首先可知直接枚举 \(m\) 这个排列,复杂度为 \(O(n + (m!)\times m^2 )\)

因为 \(m\) 很小,考虑状压 \(dp\)

\(f[s]\) 表示考虑了前 \(|s|\) 个位置,填了 \(s\) 里面的这些数。

考虑转移。

当枚举下一个位置的时候,\(pos = |s| + 1\)​ 个位置的时候,枚举新填的数,这个新填的数 会和集合中的数和集合外的数(不等于 i 的数)产生代价,

  • 对于一个前面的数 \(j\) ,从 \(i\)\(j\) 产生的代价是:\(\text{pos}\cdot k\cdot \text{cnt}[i][j]\)
  • 对于一个前面的数 \(j\) ,从 \(j\)\(i\) 产生的代价是:\(\text{pos}\cdot\text{cnt}[j][i]\)
  • 对于一个后面的数 \(j\),从 \(i\)\(j\) 产生的代价是:\(-\text{pos}\cdot \text{cnt}[i][j]\)
  • 对于一个后面的数 \(j\),从 \(j\)\(i\) 产生的代价是:\(\text{pos}\cdot k\cdot \text{cnt}[j][i]\)

转移式子就是

\(dp[s + i] = dp[s] + \sum_{j\in s} pos(k\cdot cnt[i][j] + cnt[j][i] ) + \sum_{j\notin (s + i) }pos(-cnt[i][j] + k\cdot cnt[i][j])\)

枚举 \(i\) ,再枚举 \(j\) ,复杂度就是 \(O(2^mm^2)\)

优化:

枚举 \(i\) ,把 \(i\)\(pos\) 之前的贡献预处理出来,记作 \(cost_{s, i}\),那么上面转移就不用再枚举那个 \(j\) 了,转移就可以 $2^m\times m $ 的实现。

[ATcoder] Pay to Win

进行若干操作将 \(0\) 变为 \(N\)\((N\leq 10^{18})\)

  • \(A\) 个金币把数字 \(\times\) 2
  • \(B\) 个金币把数字 \(\times\) 3
  • \(C\) 个金币把数字 \(\times\) 5
  • \(D\) 个金币把数字 \(+1\) 或者 \(-1\)

求最小需要花费的金币。

solution

\(N\leq 10^6\) 时,考虑 \(dp\) 转移。

\(f_i\) 表示将 \(i\) 拼成 \(N\) 的最小代价,这时候加减法的转移就有后效性,并且转移没有边界

考虑这么一件事情,如果这个乘了一个数,那么之后要加的数一定比这个数小。

例如: 2 * 5 + 10 这样会花费 \(5C + 10D\) 的代价,显然不如 \((2 + 2)\times 5\) 更优。

就可以把所有的加操作都放在前面,最后就省乘的转移。

设加完之后数为 \(i\) ,那么它可以转移到

i -> 2*i - 1,2 * i,2 * i + 1,3 * i - 2, 3 * i - 1, 3 * i,3 * i + 1, 3 * i + 2......

然后就可以从后向前 \(dp\)​ 了。

现在考虑 \(N\leq 10^{18}\)

\(ai + b = c (b < a)\) 那么 \(i = \lfloor \frac{c}{a} \rfloor\)

\(ai - b = c(b < a)\) 那么 \(i = \lceil \frac{c}{a} \rceil\)

这样就可以快速的考虑每个点是由那几个状态转移过来的了。

P3959 [NOIP2017 提高组] 宝藏

solution

枚举一个起点 ,设 \(f_{s}\) 表示加入的点集为 \(s\)\(s\) 内的点全部互通的最小代价,\(st_{s,j}\) 表示加入的点集为 \(s\) ,到达 \(j\) 点经过的点的个数。

这样就可以枚举一个集合内的点合集合外的点进行转移。

\[f[i|(1 << k)] = max(f[i] + dis[j][k] \times (st[i][j] + 1)) (j\in s, k \notin s) \]

\(st_{s,j}\)​ 每次转移都要先复制原来的状态,再进行转移。

树形

[HAOI2009]毛毛虫

对于一棵 \(N (N ≤ 300000)\) 个点的树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫。

求点数最多的毛毛虫。

solution

类似树形 \(dp\) 求树的直径,\(f_u\) 表示以 \(u\) 为根节点,其子树内最长的毛毛虫,转移就很简单了

\[f_u = max_{v\in son_u}f_v + 1 + max(0, cnt_u - 1) \]

剩下的和求 \(dp\) 求树的直径就一个样了。

P1472 [USACO2.3]奶牛家谱 Cow Pedigrees

一个有 \(n\) 个节点,深度为 \(k\) 的无标号完满二叉树(即每个节点的儿子数为 0 或 2)有多少种结构?定义根节点深度为 1。

答案对 9901 取模。

solution

\(f_{i, j}\) 表示 \(i\) 个点,深度为 \(j\)​​ 的方案数。枚举左右子树的节点个数和深度,这样复杂度就到了\(n^4\)

不妨设 \(f_{i, j}\) 表示 \(i\) 个节点,深度小于等于 \(j\) 的方案数,那么答案就为 \(f_{i, j} - f_{i, j - 1}\)

转移就为 \(f_{i, j} = f_{t, j - 1} + f_{i - t - 1, j - 1}\)

P1131 [ZJOI2007] 时态同步

给定一棵 \(n\)​​​ 个节点的树,并且每条边都有权值。现在有一个点向周围发射信号,使得以这个点为根,到达叶子节点的路径权值相同。

\(n\leq 5\times 10^5\)

solution

以发射信号的节点为根,因为要求到达每个叶子结点的路径权值都相等,所以这个树的每个子树同样也满足这个性质。

反过来思考,所有叶子结点到达每个子树的根的路径长度也同样相同。

所以就可以从叶子结点向上维护到根的距离就好了。

P1453 城市环路

\(n\) 个点 \(n\)​ 条边的单圈图,保证环上任意两点都只有两条路经互通,在上面做没有上司的舞会问题。(在基环树上求最大的独立集)

\(n\leq 10^5\)

solution

先写一边没有环的情况

\(f_{u, 0} = \sum max(f_{v, 1}, f_{v, 0})\)

\(f_{u, 1} = \sum f_{v, 1}\)

如果有环就找环上相邻的两个点 \(S , T\),它们一定不会同时选,把它们之间的边断掉,然后分别以 \(S, T\) 为根,跑一遍树形 \(dp\) ,找环上两点用并查集判断,最后答案 \(min(f_{s, 0}, f_{T, 0})\)

P1623 [CEOI2007] 树的匹配Treasury

给一棵树,你可以匹配有边相连的两个点,问你这棵树的最大匹配是多少,并且计算出有多少种最大匹配。

\(N\leq 10^5\)

solution

\(f_{u, 0|1}\) 表示 \(u\) 这个结点它与儿子结点形成不形成匹配,其子树内的最大匹配数量。

\(f_{u, 0} = \sum max(f_{v, 0}, f_{v, 1})\)

当该点与一个儿子形成匹配时,该儿子状态为 \(f_{v, 0}\) ,其余可以随便选。

这时,找一个 \(\Delta = f_{v,0} - max(f_{v, 0}, f_{v, 1})\) 最大儿子形成匹配就好了。

此时 \(f_{u, 1} = f_{u, 0} + 1 + \Delta\)

方案数?

先考虑没有最大的限制,求一棵树有多少种匹配。

\(f_{u,0|1}\) 表示 \(u\)​ 这个结点它与儿子结点形成不形成匹配,其子树内的匹配方案数。

\(f_{u,0} = \prod f_{v, 1} + f_{v, 0}\)

\(f_{u,1} = \sum \frac{f_{v, 0}}{f_{v, 1} + f_{v, 0}}\times f_{u, 0}\)

最大匹配的方案数

$f_{u, 1|0} $ 表示 \(u\) 这个结点它与儿子结点形成不形成匹配,其子树内的最大匹配方案数。

同上面求方案数一样,但是 \(f_{v,0}\)\(f_{v, 1}\)​ 就不能任意取了,因为 \(v\) 点选不选可能它子树内的最大匹配数会不同,我们只需要它最大匹配数的状态。

\(g_{u, 0|1}\) 表示 \(u\)​ 这个结点它与儿子结点形成不形成匹配,其子树内的最大匹配数量。

  • \(g_{v, 0} > g_{v, 1}\)\(f_{v, 0}\)

  • \(g_{v,1} > g_{v,0}\)\(f_{v, 1}\)

  • \(g_{v, 1} = g_{v, 0}\)\(f_{v,1} + f_{v,0}\)

所有取到的状态相乘就好了。

背包

P5322 [BJOI2019]排兵布阵

solution

每个城堡是独立的,所以可以单独计算每个城堡可以打败多少贡献。

\(f_{i, j}\) 表示前 \(i\) 个城堡排出 \(j\) 个士兵的最大得分。

贪心排出的士兵数恰好比一个玩家的两倍大,多了也是浪费 = = ​

预处理每个城堡的敌人排出的兵,对其排个序

同时把第一维滚掉就为

\(f_j = max(f_j, f_{j - 2\times a[i][k] - 1} +k\times i)\)

P4141 消失之物

solution

不缺少物品时候的转移为

for (int j = m; j >= w[i]; --j)
    f[j] += f[j - w[i]];

当删除一个物品的时候,就把这个物品转移过来的状态全部删掉就好了。

memcpy(g, f, sizeof f);
for(int j = w[i]; j <= m; ++j)
    g[j] -= g[j - w[i]];

P1272 重建道路

给定一棵 \(n\) 个结点的树,现割掉一些边,使得某一块连通块的大小恰好为 \(p\)

\(n\leq 150\)

solution

树上背包,设 \(f_{u,s}\) 表示 \(u\) 所在的联通块,大小恰好为 \(s\)​ 的最小代价。

这样就是把这 \(s - 1\) 恰好分配到它的每个儿子,使得总代价最小。

如果只有两个儿子的时候

\(f_{u,s} = min_{j\in[0, s - 1]}(f_{v_1,j}, f_{v_2, s - j - 1}) + (j == 0)|(s - j - 1 == 0)\)

特殊的 \(f_{u, 0} = 1\)

考虑多个儿子合并,再填上一维状态。

\(f_{u, k, s}\) 表示考虑节点 \(u\) 的前 \(k\) 个子节点了,大小为 \(s\) 的最小花费。

转移就是

\[f_{u, k, s} = min(f_{u, k - 1, s} + 1, f_{u, k - 1, s - s_v - 1} + f_{v, k_v, s_v}) \]

其中 \(k_v\) 表示 \(v\) 的所有子节点数量,\(s_v\) 表示在子树 \(v\) 上选取的点的数量。

前面表示割掉 \(v\) 这个儿子,后面表示在 \(v\) 选一定的点。

滚掉 \(k\) 就成了

\[f_{u, s} = min(f_{u, s} + 1, f_{u, s - s_v} + f_{v, s_v}) \]

这时候 \(s\)​ 要倒叙枚举,因为每次转移都要用到 \(f_{u, s - s_v}\)​ 在上一个儿子时的状态,把它放在后面更新就好可以了。

关于答案:显然不能直接输出 \(f[1][p]\) 因为 \(1\) 号节点不一定选,对所有的 \(f[i][p]\) 取个 \(min\) 然后 +1(要断开父亲那条边)

loj 6989 小y的背包计数问题

\(y\) 有一个大小为 \(N\) 的背包,并且小 \(Y\)\(N\) 种物品。

对于第 \(i\) 种物品,共有 \(i\) 个可以使用,并且对于每一个 \(i\) 物品,体积均为 \(i\) ,求把背包装满的方案数。

方案不同:当且仅当至少存在一种物品的使用数量不同。

\(N\leq 10^5\)

solution

\(f_{i, j}\) 表示前 \(i\) 个物品,体积为 \(j\) 的方案数。

\(f_{i, j} = \sum f_{i - 1, j - i\times k}\)

复杂度 \(O(n^3)\)

优化:前缀优化。

转移都是从前面 \(i\) 的倍数转移过来的,把 mod i 相同的分到一组,预处理一个前缀数组 \(g_{i - 1, j} = f_{i - 1, j -i} + f_{i - 1, j - 2\times i}\dots\)

这样就实现了 \(O(n)\) 的转移

P3216 [HNOI2011]数学作业

给定正整数 \(n,m\) ,要求计算 \(\text{Concatenate}(n) \bmod \ m\) 的值,其中 \(\text{Concatenate}(n)\) 是将 \(1 \sim n\) 所有正整数 顺序连接起来得到的数。

solution

这主要是学到了一种写矩阵快速幂的方法。

先递推式子。

\(f_{i + 1}\) 表示前 \(i + 1\) 个数组成的数。

那么递推式为 \(f_{i + 1} = f_{i} \times 10^k + i + 1\) ,其中 \(k\)\(i + 1\) 的位数。

如果 \(k\)​​ 固定,可以写矩阵。(把推到得状态写在下面,原状态在左边)

这么一个框架:

1:   1 1 1                            1 1 1
i:   0 1 1       ->  [1, i, f(i)] *  0 1 1
f(i):0 0 10^k                        0 0 10^k
     1 i+1 f(i+1)    

然后这个 \(k\) 其实并不固定,所以可以把 \(k\) 相同得放在一个矩阵里,分组进行就好了。

序列

[HNOI2010]合唱队

solution

因为 \(a\) 从前向后插入的时候只能加在 \(b\) 序列的最左边或最右边。

那么 \(a\) 的一个前缀一定对应着 \(b\) 序列中的一段连续的区间。

\(f_{i, j,0|1}\)​​ 表示能形成 \(b\)​​ 序列区间 \(i\sim j\)​ ,最后加入的数在左边还是右边的 \(a\)​ 序列​前缀的方案数。

枚举边界进行转移就好了。

P4302 [SCOI2003]字符串折叠

solution

\(f_{i, j}\) 表示区间 \(i\sim j\) 折叠之后的最短长度。

考虑转移。

两种情况:

  • 总串是由若干个子串拼起来的,枚举断点。

\[f_{l, r} = min(f_{l, k} + f_{k + 1, r}) \]

  • 总串是由一个串重复若干次的来的。

\[f_{i, j} = min(f_{l, l + k - 1} + 2 + g(k)) \]

其中 2 是两个括号的长度,\(g(k)\) 表示这个字串折叠后的最短长度。

同时 \(k\) 要满足是 \(r - l + 1\)​ 的因子,同时要判断枚举的字串是否能重复得到该区间。

暴力判断就好了。

AT2558 [ARC073D] Many Moves

solution

线段树优化 \(dp\) 转移

\(f_{i,j}\)​ 表示执行了 \(i\)​​ 次操作,两个点的分别在 \(x_i\)​ (其中一个点一定会在 \(x_i\))和 \(j\)​ 的最小代价。

转移:

\(f_{i + 1, j} = min(f_{i, j} + |x_i - x_{i + 1}|)\)​​

\(f_{i + 1, x_i} = min(f_{i, j} + |j - x_{i + 1}|)\)

优化:

线段树滚掉第一维

\(j\) 为下标,\(f_j\) 为权值建一棵线段树,对于第一个操作就是全局 \(+|x_i - x_{i + 1}|\)

对于第二个操作,分类讨论把绝对值拆开,就成了:

\[j > x_{i + 1}:~~ f_{i + 1, x_i} = min(f_{i, j} + j) - x_{i + 1}\\ j < x_{i + 1}: ~~ f_{i + 1, x_i} = min(f_{i, j} - j) - x_{i + 1} \]

问题就变成了查询区间 \([j, n] (j > x_i)\) 中的 \(f_{i, j} + j\) 和查询 $[1, j](j < x_i) $ 中 \((f_{i, j} - x_{i + 1})\) 的最小值了。

所以在线段树上只需要维护 \(f_{j} + j\)\(f_j - j\) 就好了。

复杂度:\(O(n + Q logn)\)

CF713C Sonya and Problem Wihtout a Legend

给定一个有 \(n\) 个正整数的数组,一次操作中,可以把任意一个元素加一或者减一。(元素可被减至负数或 \(0\)),求使得原序列严格递增的求最小操作次数。

solution

严格单调递增转化为非严格单调递增(如果不转化没法对 \(a_i\) 离散化):

\(a_i < a_{i + 1}\)

得到 \(a_i \leq a_{i + 1} - 1\)

两边同时减 \(i\) : \(a_i - i \leq a_{i + 1} - (i + 1)\)

\(b_i = a_i - i\)

那么上式就可以转化为 \(b_i \leq b_{i + 1}\)

可以得到这么个结论:将 \(a_i - i\) 那么序列就会成为一个非严格单调递增的。

可以先求出一个非严格单调递增的最后都加上 \(i\) 就可以了。

考虑 \(dp\) 怎么实现

\(f_{i, j}\) 表示前 \(i\) 个数,第 \(i\) 个数为 \(j\) 的最小代价。

转移: \(f_{i, j} = |b_i - j| + min_{k\leq j}(f_{i - 1, k})\)

后面那一项是个前缀最小值,可以直接预处理出来。

\(g_{i, j} = f_{i, j} + g_{i, j - 1}\)

转移就成了 \(f_{i, j} = |b_i - j| + g_{i - 1, j}\)

[HAOI2008]木棍分割

\(n\)​ 个数,每个数权值为 \(a_i\) ,现在要把他们划分成 \(m\)​ 段,求每段权值之和最大值最小是多少,并求出在该情况下划分的方案数。

\(n\leq 50000\)​​,\(0 \leq m \leq min(n - 1, 1000)\)​​,\(1\leq a_i\leq 1000\)​​

solution

第一问直接二分答案就好了。

第二问,根据第一问二分的答案 \(x\) 划分。

\(f_{i, j}\)​ 表示前 \(i\)​ 个数划分了 \(j\)​ 段的方案数。

转移:\(f_{i, j} = f_{i,j} + \sum^{k \leq L_i}_{k = 1}f_{i - k, j - 1}(\sum^{c = i}_{c = i- L_i+ 1} a_c \leq x)\)

复杂度: \(O(m\times n ^ 2)\)

考虑优化:

\(\sum^{k \leq L_i}_{k = 1}f_{i - k, j - 1}(\sum^{c = i}_{c = i- L_i+ 1} a_c \leq x)\) 这一坨玩意其实是个区间和,可以用前缀和搞一搞

\(sum_{i, j} = \sum^{k\leq j}_{k = 1} f_{i, k}\)

转移式子就可以写成 \(f_{i, j} = sum_{i - 1, j} - sum_{i - 1, k - 1}\)

这样其实复杂度一点没变,因为还要枚举一个 \(k\),以为 \(sum\) 满足单调性,所以可以直接用双指针处理出每个点前面的 \(k\) 在哪儿就好了。

复杂度:\(O(nm + nlog(n\times a_i))\)

P5999 [CEOI2016]kangaroo

生成一个长度为 \(n\) 的排列,给定排列的第一个和最后一个元素,同时要求该排列的每个数至少都满足以下的一条性质:

  • \(a_{i} < a_{i + 1}~ \&\&~a_{i} < a_{i - 1}\)
  • \(a_i > a_{i + 1}~\& \&~a_i > a_{i - 1}\)

求有多少种合法的排列 ?

一道奇妙的 \(dp\) 题。

solution

插数 \(dp\)

我们可以从小到大向序列中插数。

状态:\(f_{i, j}\) 表示插到了第 \(i\) 个数,将序列分成了 \(j\) 段,并且每一段都符合条件的方案数。

转移:

  • \(i\neq s~\&\&~i\neq t\)

\(f_{i, j} = (j - (i > s) - (i > t)) \times f_{i - 1,j - 1} + j\times f_{i - 1,j + 1}\)

当插入的数不是起点或终点的时候。

插入的数可以插入原来两段之间把两段连起来,也可以单独成段。

注意: 当插入的数比起点大的时候不能插入到最左边,因为数是从小到大插入的,如果比起点大证明起点已经插入了,如果比终点大的时候同理也不能插入到最右边。

  • \(i = s~\or~i=t\)

\(f_{i, j} = f_{i - 1, j - 1} + f_{i - 1, j}\)

当插入的时候是起点或终点的时候。

插入的数只能放在两边,可以单独成段,也可以与已有的段合并。

注意转移的时候段数,很容易出错。

P6280 [USACO20OPEN]Exercise G

solution

读懂题太不容易了 /kk

把操作放在图上,移动的轨迹就是一个个的环,每个奶牛都在某个环上移动,若操作次数是某个环长的倍数,那么这个环上的奶牛都会回到起点,如果想让所有奶牛都回到起点,就是所有环长度的倍数。求操作次数最少,就是求每个环的最小公倍数。

假如设每个环的长度是 \(a_i\),那么可以发现 \(k=\operatorname{lcm}a_i\) ,又知道 \(\operatorname{lcm}\) 其实就是将那几个数分解质因数,然后取每个质数的最高次幂乘起来,所以我们可以想到枚举素数来做 \(dp\)

我们设 \(f_{i,j}\) 表示前 \(i\) 个素数总和为 \(j\) 的所有 \(k\) 的总和,枚举第 \(i\) 个素数的幂进行转移,因为之前并没有用过第 \(i\) 个素数,所以应把上一个状态乘上 \(p_i^k\) ,所以直接有方程 \(f_{i,j}=\sum f(i-1, j-p_i^k)\times p_i^k\)

接着发现这个东西可以滚动数组压缩一下,于是可以省掉一维 \(f(j)=\sum f(j-p_i^k)\times p_i^k\) 倒序枚举即可,初始状态 \(f(0)=1\),最后答案是 \(\sum f(i)\)​。

雾。。

CF348D Turtles

\(n \times m\) 的网格图,某些各自上有一些障碍,求从 (1, 1) 到 (n, m) 有多少对不相交的且不经过障碍的两条路径。

solution

容斥 dp

不考虑相交的话,方案数就是一个最朴素不过的dp

\(f_{i,j} = 0~(a_{i,j} = 0)\)

\(f_{i, j} = f_{i,j} + f_{i - 1, j - 1} + f_{i,j -1}~(a_{i, j} = 1)\)

求相交的情况。(见题解吧,弄图有点麻烦)

题解

矩阵优化

P2579 [ZJOI2005]沼泽鳄鱼

建议更换题面,哪来的鳄鱼?

solution

先不考虑鳄鱼的情况,求起点到终点走 \(k\) 步的方案数。

\(f_{i,j}\) 表示走了 \(i\) 步,到达 \(j\) 的方案数。

转移: \(f_{i,j} = \sum f_{i - 1, k}\times A_{k, j}\) 其中 \(A\) 表示 \(k\) 能否到达 \(j\)

题目中要求的走的步数很大,所以上面的式子可以用矩阵加速。

有鳄鱼?

发现 \(2\leq T \leq 4\) ,所以所有鳄鱼运动的总周期为12。可以计算 12 种变化矩阵 (有鳄鱼的标为 0),表示每个周期可以走的边,然后乘起来,然后求乘积的 \(\lfloor \frac{K}{12} \rfloor\) 次幂,乘以前的 \((K~mod~12)\) 种变化的乘积,就是总变化矩阵。用原矩阵乘以该矩阵就是答案,

复杂度:\(O(N^3 log K)\)

posted @ 2021-08-14 17:16  Dita  阅读(89)  评论(0编辑  收藏  举报