CF1523E - Crypto Lights(概率dp,組合數學)
阿新 • • 發佈:2021-09-10
題目
有\(n\)盞燈排成一排,初始時所有的燈是滅的。每次會等概率隨機點亮一個滅的燈。如果存在連續\(k\)盞燈中有大於1盞燈是亮的,則結束。給定\(n\)和\(k\),問期望的燈的數量是多少。
題解
假設有\(i\)盞燈被點亮,那麼它們的間隔均大於等於\(k\)。故\(i\)盞燈被點亮的順序不重要,確定了這\(i\)盞燈的位置,任意一種取的順序都是合法的。設\(f_i\)代表第\(i\)盞燈被點亮依舊合法的情況數,那麼有\(i\)盞燈被點亮後依舊合法的概率\(g_i\)為
\[g_i=(\prod_{j=0}^{i-1}\frac{1}{n-j} )i! \cdot f_i \]又有
\[g_i=g_{i+1}+P(第i+1盞燈點亮後不合法) \]故答案為
考慮計算\(f_i\),即在1~n中選擇\(i\)個數\((1\le a_1< a_2<...<a_i \le n)\)使得它們之間間隔大於等於\(k\)的方案數。作差分有\(b_j=a_{j}-a_{j-1}\),那轉換為\(b_1 \ge 1\),\(b_j\ge k\),\(\sum{b_j} \le n\)。直接轉換為盒子放球模型解決,相當於\(n-(i-1)\cdot k\)個球放入\(i+1\)個盒子,除了第1個盒子至少放一個,其餘盒子可放可不放,有
\[f_i=C(n-(i-1)\cdot k + i - 1, i) \]#include <bits/stdc++.h> #define endl '\n' #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0) #define mp make_pair #define seteps(N) fixed << setprecision(N) typedef long long ll; using namespace std; /*-----------------------------------------------------------------*/ ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} #define INF 0x3f3f3f3f const int N = 3e5 + 10; const int M = 1e9 + 7; const double eps = 1e-5; ll fact[N], rfact[N]; inline ll qpow(ll a, ll b, ll m) { ll res = 1; while(b) { if(b & 1) res = (res * a) % m; a = (a * a) % m; b = b >> 1; } return res; } ll C(int n, int m) { if(m > n) return 0; return fact[n] * rfact[n - m] % M * rfact[m] % M; } ll p[N]; int main() { IOS; fact[0] = rfact[0] = 1; for(int i = 1; i < N; i++) { fact[i] = fact[i - 1] * i % M; rfact[i] = rfact[i - 1] * qpow(i, M - 2, M) % M; } int t; cin >> t; while(t--) { int n, k; cin >> n >> k; for(int i = 1; i <= n + 1; i++) p[i] = 0; ll pw = 1; for(int i = 1; i <= n; i++) { pw = pw * qpow(n - i + 1, M - 2, M) % M; int ret = n - (i - 1) * k; if(ret < 0) break; p[i] = C(ret + i - 1, i) * pw % M * fact[i] % M; } ll ans = 0; for(int i = 1; i <= n; i++) { ans = (ans + (i + 1) * (p[i] - p[i + 1]) % M + M) % M; } cout << ans << endl; } }