CF1523E - Crypto Lights(概率dp,组合数学)
题目
有\(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盏灯点亮后不合法)
\]
故答案为
\[\sum\limits_{i=1}^{n}{(g_i-g_{i+1})(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;
}
}