很久以前就用线段树过了这题,现在刚学了cdq又用cdq写了一遍这题,所以就把两种做法都讲一下。
以下的 $n$ 指 $R$,$m$ 指 $C$。
朴素做法
一眼的递推,$f_{i,j}$ 表示走到格子 $(i,j)$ 的方案数,$a_{i,j}$ 为格子 $(i,j)$ 的标号。
时间复杂度 $O(n^2m^2)$,需要优化。
线段树
建 $nm$ 棵线段树,第 $k$ 棵线段树维护 $a_{i,j}=k$ 的 $f_{i,j}$ 值。我们从小到大枚举行号,这样就不用考虑行的限制。
若当前计算到了 $i$ 行,第 $0$ 棵线段树维护了如下的序列:
第 $k$ 棵线段树维护了如下的序列:
计算第 $i$ 行时,每个 $f_{i,j}$ 的答案即为第 $0$ 棵线段树中区间 $[1,j-1]$ 的和减去第 $a_{i,j}$ 棵线段树区间 $[1,j-1]$ 的和。
线段树要动态开点,时间复杂度 $O(nm\log m)$。
1 |
|
CDQ分治
观察那个 $(x<i,y<j,a_{i,j}\ne a_{x,y})$,除去最后那一个不等于的限制,这不就一个三维偏序嘛。
cdq分治的时候 $i$ 这一维天然有序,$j$ 这一维 cdq 分治削掉,$a_{i,j}$ 那维是树状数组维护,所以即使是不等号也没关系。
cdq分治里 $g_k$ 表示目前区间内所有 $a_{i,j}=k$ 的 $f_{i,j}$ 总和,$g$ 的清空需要时间戳。
时间复杂度 $O(nm\log n)$。
1 |
|
做法对比
第一条结果为 cdq 分治,第二条为动态开点线段树。
可以看到,无论耗时,内存,码量,cdq全方位吊打线段树。