1. 程式人生 > 其它 >面向物件3(Java)

面向物件3(Java)

題目大意

一個 \((n + 2) \times m\) 的網格。

除了第一行與最後一行,每一行都有 \(p\) 的概率消失,求 \(k\) 天后,網格始終保持聯通的概率。

答案對 \(10^9 + 7\) 取模。

\(\text{Data Range:} 1 \leq n,m \leq 1.5 \times 10^3, k\leq 10^5\)


不難發現最後每一行剩下的一定都會是一個連續的區間,而且每行之間相互獨立。

自然設立狀態 \(dp_{i,l,r}\) 表示當前是第 \(i\) 行,\(k\) 天后這一行剩下的區間為 \([l, r]\),第 \(0\) 行到第 \(i\) 行都聯通的概率。

先考慮算變成區間 \([l,r]\) 的概率。

\(k\) 天內,消失 \(i\) 個格子的概率為 \(g_i = \binom{k}{i} p^i \times (1-p)^{k-i}\)

所以剩下區間 \([l,r]\) 的概率為 \(g_{l-1} \times g_{m-r}\)

當然還需要檢查這段區間長度是否合法,因為最多消失 \(2k\) 個格子。

轉移式子不難寫出。

\[dp_{i,l,r} = g_{l-1} \times g_{m-r} \sum_{[l_1, r_1] \cap [l,r] \neq \varnothing} dp_{i-1,l_1,r_1} \]

複雜度 \(nm^4\)

,顯然不行,而且狀態數都為 \(nm^2\)

那考慮優化狀態,去掉左端點的限制。

\(dp_{i,r}\) 表示第 \(i\) 行的剩餘區間的右端點為 \(r\),第 \(0\) 行到第 \(i\) 行都聯通的概率,轉移的時候列舉 \(l\) 進行轉移。

考慮一個和區間 \([l,r]\) 有交的區間 \([l_1,r_1]\)

它滿足 \(r_1 \geq l, l_1 \leq r\),那麼容斥把這一部分算出來即可。

對於 \(r_1 < l\) 的部分,列舉 \(l\) 時計算 \(\sum_{j=1}^{l-1} dp_{i-1,j}\)

對於 \(l_1 > r\)

的部分,因為網格是對稱的,所以 \(f_{i,r}\) 也可以表示左端點為 \(m-r+1\) 且聯通的概率,因此這一部分可以計算為 \(\sum_{j=1}^{m-r} f_{i-1,j}\)

於是轉移如下。

\[dp_{i,r} = \sum_{l=1}^r g_{l-1} g_{m-r} (\sum_{j=1}^m f_{i-1, j} - \sum_{j = 1}^ {i-1} f_{i-1,j} - \sum_{j=1}^{m-r} f_{i-1,j}) \]

這東西長得就很一臉字首和的樣子,那就考慮字首和優化。

\(s_{j}\) 表示 \(\sum_{k=1}^j f_{i-1,k}\)

\[f_{i,r} = g_{m-r} \sum_{l=1}^r g_{l-1} )(s_m - s_{l-1} - s_{m-r}) \] \[f_{i,r} = g_{m-r} \times( (s_m - s_{m-r}) \sum_{l = 1}^r g_{l-1} - \sum_{l=1}^r g_{l-1} s_{l-1}) \]

在令 \(p_i\)\(q_i\) 依次為 \(g_i\)\(g_i \times s_i\) 的字首和,之後即可做到 \(O(1)\) 轉移,總時間複雜度 \(o(nm)\)

// 德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛
// 德麗莎的可愛在於德麗莎很可愛,德麗莎為什麼很可愛呢,這是因為德麗莎很可愛!
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
  int x = 0, f = 1;  char ch = getchar();
  while( !isdigit(ch) ) { if(ch == '-') f = -1;  ch = getchar();  }
  while( isdigit(ch) ) {  x = (x << 1) + (x << 3) + (ch ^ 48);  ch = getchar();  }
  return x * f;
}
const int N = 3e6, mod = 1e9 + 7;
int n, m, a, b, pt, pt2, k, ans, fac[N], ifac[N], dp[3005][3005], g[3005];
int power(int a,int b) { int ans = 1; while (b) { if (b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1;} return ans; }
int binom(int n,int m) { return fac[n] * ifac[m] % mod * ifac[n - m] % mod; }
int s[N], p[N], q[N];
signed main () {
  n = read(), m = read(), a = read(), b = read(), k = read(); 
  fac[0] = 1; for (int i = 1; i <= k; i++) fac[i] = fac[i - 1] * i % mod;
  ifac[k] = power(fac[k], mod - 2); for (int i = k - 1; ~i; i--) ifac[i] = ifac[i + 1] * (i + 1) % mod;
  pt = a * power(b, mod - 2) % mod; pt2 = 1 - pt; pt2 += mod; pt2 %= mod;
  for (int i = 0; i <= k && i <= m; i++) g[i] = binom(k, i) * power(pt, i) % mod * power(pt2, k - i) % mod;
  dp[0][m] = 1;  p[0] = g[0]; 
  for (int i = 1; i <= m; i++) p[i] = p[i - 1] + g[i], p[i] %= mod;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) s[j] = s[j - 1] + dp[i - 1][j], s[j] %= mod;
    for (int j = 1; j <= m; j++) q[j] = q[j - 1] + g[j] * s[j] % mod, q[j] %= mod;
    for (int r = 1; r <= m; r++) dp[i][r] = g[m - r] * ( (s[m] - s[m - r] + mod) % mod * p[r - 1] % mod - q[r - 1] + mod) % mod;
  }  
  for (int i = 1; i <= m; i++) { ans += dp[n][i]; ans %= mod;}
  printf("%lld\n", ans);
  return 0;
}