CF2180F2. Control Car (Hard Version) 题解
题目链接:https://codeforces.com/problemset/problem/2180/F2
思路
让每个顶点独立、等概率地选择方向。设小车停在网格内的概率为 \(p\),则答案为
\[4^{(n+1)(m+1)}p.
\]
以下概率均在模 \(10^9+7\) 意义下计算,除法使用逆元。
1. 压缩状态
对于当前格子,记左下、右下、右上顶点分别为 \(a,b,c\)。
- 能向下移动,当且仅当 \(a\) 不朝右且 \(b\) 不朝左。
- 下方被挡住时,能向右移动,当且仅当 \(b\) 不朝上且 \(c\) 不朝下。
由于小车只会向下、向右移动,右下顶点 \(b\) 总是新的顶点。在这三个相关顶点中:
- 从左边进入时,只有 \(a\) 已经被检查过,只需区分它是否朝右。
- 从上边进入时,只有 \(c\) 已经被检查过。上一步能够向下,说明这个顶点不朝左,因此它在上、下、右三个方向中等概率分布。
于是只有三种状态:
- \(x\):从左进入,左下顶点朝右。
- \(y\):从左进入,左下顶点不朝右。
- \(z\):从上进入。
枚举相关顶点的方向,得到:
| 当前状态 | 向下进入 \(z\) | 向右进入 \(x\) | 向右进入 \(y\) | 原地停止 |
|---|---|---|---|---|
| \(x\) | \(0\) | \(\frac3{16}\) | \(\frac38\) | \(\frac7{16}\) |
| \(y\) | \(\frac34\) | \(0\) | \(\frac3{16}\) | \(\frac1{16}\) |
| \(z\) | \(\frac9{16}\) | \(\frac1{24}\) | \(\frac5{24}\) | \(\frac3{16}\) |
2. 按列转移
设当前列第 \(i\) 行从左进入的两种状态概率为 \(x_i,y_i\),从上进入的概率为 \(z_i\)。有
\[z_1=0,\qquad
z_{i+1}=\frac34y_i+\frac9{16}z_i.
\]
转移到下一列后:
\[x_i'=\frac3{16}x_i+\frac1{24}z_i,
\]
\[y_i'=\frac38x_i+\frac3{16}y_i+\frac5{24}z_i.
\]
再增加一个吸收状态 \(s\),表示已经停在网格内的概率:
\[s'=s+\sum_{i=1}^{n}
\left(\frac7{16}x_i+\frac1{16}y_i+\frac3{16}z_i\right).
\]
向下离开第 \(n\) 行的概率直接丢弃。
因此,向量
\[(x_1,y_1,x_2,y_2,\ldots,x_n,y_n,s)
\]
可以通过一个 \(2n+1\) 阶矩阵完成整列转移。枚举每个基向量,按照上述递推处理一列,即可构造矩阵。
初始时,可以把起点看作从左进入:
\[x_1=\frac14,\qquad y_1=\frac34,
\]
其余状态为零。进行 \(m\) 次整列转移后,\(s\) 就是所求概率;此时仍向右移动的概率对应从右边界离开,不计入答案。
使用矩阵快速幂即可。按上述状态顺序,矩阵是下三角矩阵,代码只计算下三角部分。
时间复杂度: \(O(n^3\log m)\)。
空间复杂度: \(O(n^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int mod = 1e9 + 7;
int T, n, k, a[105][105], b[105][105], f[105], g[105];
ll m, inv4, inv16, inv24;
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void build() {
memset(a, 0, sizeof(a));
for (int j = 0; j < k - 1; j++) {
ll z = 0;
for (int i = 0; i < n; i++) {
int x = j == 2 * i, y = j == 2 * i + 1;
a[2 * i][j] = (3 * x * inv16 + z * inv24) % mod;
a[2 * i + 1][j] = ((6 * x + 3 * y) * inv16 + z * 5 * inv24) % mod;
a[k - 1][j] = (a[k - 1][j] + (7 * x + y) * inv16 + z * 3 * inv16) % mod;
z = (3 * y * inv4 + z * 9 * inv16) % mod;
}
}
a[k - 1][k - 1] = 1;
}
void calc() {
memset(g, 0, sizeof(g));
for (int i = 0; i < k; i++) {
for (int j = 0; j <= i; j++) {
g[i] = (g[i] + (ll)a[i][j] * f[j]) % mod;
}
}
memcpy(f, g, sizeof(f));
}
void mul() {
memset(b, 0, sizeof(b));
for (int i = 0; i < k; i++) {
for (int j = 0; j <= i; j++) {
if (!a[i][j]) continue;
for (int l = 0; l <= j; l++) {
b[i][l] = (b[i][l] + (ll)a[i][j] * a[j][l]) % mod;
}
}
}
memcpy(a, b, sizeof(a));
}
int main() {
inv4 = qpow(4, mod - 2);
inv16 = qpow(16, mod - 2);
inv24 = qpow(24, mod - 2);
scanf("%d", &T);
while (T--) {
scanf("%d%lld", &n, &m);
k = 2 * n + 1;
build();
memset(f, 0, sizeof(f));
f[0] = inv4;
f[1] = 3 * inv4 % mod;
ll x = m;
while (x) {
if (x & 1) calc();
mul();
x >>= 1;
}
printf("%lld\n", f[k - 1] * qpow(4, (n + 1) * (m + 1)) % mod);
}
return 0;
}
浙公网安备 33010602011771号