POJ 3744「Scout YYF I」:一維隨機游走,位置 i 到 i+1 概率 p,到 i+2 概率 1-p。某些位置有陷阱(踩中失敗)。求從 0 走到 n 不踩陷阱的概率。n 極大 (10⁹),陷阱數 m ≤ 10。本文解析區間矩陣快速冪優化 DP。
設 dp[i] = 從 0 安全到達 i 的概率。
dp[i] = p * dp[i-1] + (1-p) * dp[i-2] (i ≥ 2)
初始:dp[0]=1, dp[1]=p (假設 1 無陷阱)。
[dp[i], dp[i-1]]^T = M * [dp[i-1], dp[i-2]]^T
M = [[p, 1-p], [1, 0]]
區間 [L, R] 無陷阱時:[dp[R], dp[R-1]]^T = M^(R-L) * [dp[L], dp[L-1]]^T
dp[0]=1, dp[1]=p (若 1 是陷阱則 dp[1]=0)。dp[b]、dp[b-1]。p*dp[b] + (1-p)*dp[b-1],安全通過 c 需跳過 → dp[c+1] = (1-p)*dp[b] (從 b 跳 2 步)。dp[n] (或最後安全位置的概率)。struct Mat {
double a[2][2];
Mat() { memset(a, 0, sizeof(a)); }
static Mat identity() { Mat I; I.a[0][0]=I.a[1][1]=1; return I; }
};
Mat mul(const Mat& A, const Mat& B) {
Mat C;
for (int i=0;i<2;++i)
for (int k=0;k<2;++k)
for (int j=0;j<2;++j)
C.a[i][j] += A.a[i][k] * B.a[k][j];
return C;
}
Mat mpow(Mat base, long long exp) {
Mat res = Mat::identity();
while (exp) {
if (exp & 1) res = mul(res, base);
base = mul(base, base);
exp >>= 1;
}
return res;
}
int main() {
int n, m; double p;
while (scanf("%d%lf", &n, &p) == 2) {
vector trap(m);
for (int i=0;i 0) {
Mat Mp = mpow(M, len);
double ndp0 = Mp.a[0][0]*dp0 + Mp.a[0][1]*dp1;
double ndp1 = Mp.a[1][0]*dp0 + Mp.a[1][1]*dp1;
dp0 = ndp0; dp1 = ndp1;
}
// 經過陷阱 t: 從 t-1 跳1步(p) 或 t-2 跳2步(1-p) 會踩中
// 安全到達 t+1: 必須從 t-1 跳2步 (1-p)*dp[t-1]
double safe = (1-p) * dp1; // dp1 是 dp[t-1]
dp0 = dp1; // dp[t] = 0 (陷阱)
dp1 = safe; // dp[t+1]
prev = t;
}
// 最後一段到 n
int len = n - prev - 1;
if (len > 0) {
Mat Mp = mpow(M, len);
double ndp0 = Mp.a[0][0]*dp0 + Mp.a[0][1]*dp1;
dp0 = ndp0;
}
printf("%.7lf\n", dp0);
}
return 0;
}
dp[0]=1,dp[1]=p (從 0 跳 1 步)。若 1 是陷阱則 dp[1]=0。%.7lf。本題是「線性遞推 + 稀疏障礙 + 巨大步數」的標準模式:矩陣快速冪跳過無障礙區間,逐個處理障礙點。核心技巧:
M^(a+b) = M^a * M^b 分段計算。此模板適用於所有「線性遞推 + 少量特殊位置修正 + 大 n」問題。