CF1623D 题解

· · 题解

CF1623D 题解

Upd:修好了挂掉的 Latex ——3/7/2022

题意

给一个 n\times m 大小的地图和一个以 (r_b,c_b) 为起点的机器人,机器人每次会走一步,

走的方式是将当前其所在的坐标 (r,c) 变成 (r+dr,c+dc),其中初始时 dr=dc=1

每当机器人下一步的横坐标 r 大于 n 或小于 1 时将 dr 变成 -dr

当机器人下一步的纵坐标 c 大于 m 或小于 1 时将 dr 变成 -dc

同时,机器人每到一个新的位置时(初始位置也算到了一个新位置),

就会有 p 的概率将所有横坐标等于 r 的点和纵坐标为 c 的点一起覆盖。

问特殊点 (r_d,c_d) 第一次被覆盖时,机器人走的期望步数。

做法

显然,机器人走的路径对应的位置序列虽然无限长,但是存在一个长度不超过 4nm 的循环节,

因为一个机器人在某个位置时,其以后如何移动只与其坐标和移动方向有关,

那么用四元组 (dr,dc,r,c) 表示一个机器人的位置和方向。又因为 drdc 只有 4 种不同的可能,

则如果两次机器人的位置和方向对应四元组完全相等,则两次以后的所有行动也都完全相等,

这样就产生了一个循环节。

而两两不完全相同的四元组最多有 4nm 个,故循环节长度不超过 4nm

那么我们记一个循环节中,机器人第 i 次有机会覆盖到特殊点时,数 t_i 等于:

从这个循环节的起点到当前机器人所在位置,机器人走的步数。

定义机器人在某个位置 (r,c) 时有机会覆盖到特殊点,当且仅当 r=r_d c=c_d

我们再记最终答案为 T,不覆盖的概率 q=1-p,循环节长度为 z

一个循环节中有 k 个位置使得机器人有机会覆盖到特殊点,则我们有:

含义是: 从某一个循环节开始,我们第一次有 $p$ 的概率覆盖特殊点,这时一共走了 $t_1$ 步; 同时,我们有 $q=1-p$ 的概率不覆盖特殊点,我们就需要继续走, 直到我们第二次有机会覆盖特殊点,我们又有 $p$ 的概率覆盖特殊点,这时我们一共走了 $t_2$ 步, 我们也有 $q$ 的概率不覆盖特殊点,我们就又需要继续走,这样的过程一直进行, 一直到我们第 $k$ 次有机会覆盖特殊点,此时我们有 $p$ 的概率覆盖特殊点,这时一共走了 $t_k$ 步, 我们同样有 $q$ 的概率不覆盖特殊点,我们就又需要继续走, 这时我们走完了一个循环节,已经走了 $z$ 步后进入下一个循环节, 而下一个循环节,在加上一个循环节的 $z$ 步后,就和上面的过程相似了。 我们发现,既然下一个循环节的过程和这个循环节相似, 那么,在减掉这个及之前的循环节所走的步数后,下一个循环节的答案还是 $T$。 那么我们也许可以尝试用答案 $T$ 去表达自己,即: $T=pt_1+q(pt_2+q(pt_3+q(\cdots q(pt_k+q(z+T))\cdots)))$。 写成和式的形式,就是: $T=(\sum\limits_{i=1}^{k}q^{i-1}pt_i)+q^{k}(z+T)$。 这个就很像一个关于 $T$ 的方程,我们解一下就可以得到: $(1-q^k)T=(\sum\limits_{i=1}^{k}q^{i-1}pt_i)+q^{k}z$,即 $T=\frac{(\sum\limits_{i=1}^{k}q^{i-1}pt_i)+q^{k}z}{1-q^k}$。 因为模数是质数,所以我们只需要处理逆元后就可以算了。 时间复杂度是 $O(k)$ 的,而 $k\leq z\leq 4nm$,故可以通过。 当然,我写的代码里用了 STL map 记录一个四元组是否出现过, 这样比较方便,但复杂度就带了 $\log$,而实际上可以直接用数组记录,复杂度就是线性。 ``` #define mp make_pair #define int long long #define pii pair < int , int > #define rep(i, a, b) for (int i = (a); i <= (b); i++) // 快读就不放了 const int N (1e6 + 10); const int mod (1e9 + 7); int ksm (int a, int b) { int r = 1; for (; b; a = a * a % mod, b >>= 1) if (b & 1) r = r * a % mod; return r; } int t[N]; map < pair < pii, pii > , int > vis; int n, m, k, rb, cb, rd, cd, p, P, q, z; void work() { vis.clear(); n = read(), m = read(), rb = read(), cb = read(); rd = read(), cd = read(), P = read(); p = P * ksm (100, mod - 2) % mod, q = (mod + 1 - p) % mod; int dr = 1, dc = 1; k = z = 0; if (rb + dr > n || rb + dr <= 0) dr = -dr; if (cb + dc > m || cb + dc <= 0) dc = -dc; for (int i = 0; ; i++) { vis[mp (mp (rb, cb), mp (dr, dc))] = 1; if (cd == cb || rd == rb) t[++k] = i; rb += dr, cb += dc, z++; if (rb + dr > n || rb + dr <= 0) dr = -dr; if (cb + dc > m || cb + dc <= 0) dc = -dc; if (vis.count (mp (mp (rb, cb), mp (dr, dc)))) break; } int inv = mod + 1 - ksm (q, k); inv %= mod, inv = ksm (inv, mod - 2); int ans = z * ksm (q, k) % mod * inv % mod; rep (i, 1, k) ans = (ans + (t[i] * p % mod * inv % mod * ksm (q, i - 1) % mod) % mod) % mod; cout << ans << endl; } signed main() { int t = read(); while (t--) work(); return 0; } ```