题意
n n n盏熄灭的灯,每次操作在熄灭的灯随机选择一盏灯打开
当任意连续 k k k个灯至少有 2 2 2盏灯被打开游戏结束
求游戏结束的期望操作次数。
对于一个打开 w w w盏灯结束游戏的方案,考虑分别计算它的 w w w次贡献
分别是第 1 , 2 , . . . w 1,2,...w 1,2,...w次打开灯的贡献
基于这样的思想,我们考虑枚举一个 p p p表示当前点亮了 p p p盏灯
考虑计算此时游戏没有终止的方案数记作 f f f
那么对期望的贡献即为 f ∗ p ! n ∗ ( n − 1 ) . . . ∗ ( n − p + 1 ) f*\frac{p!}{n*(n-1)...*(n-p+1)} f∗n∗(n−1)...∗(n−p+1)p!
如何计算 f f f呢??
这个问题的实质是,选出 p p p盏灯点亮,使得任意两盏亮灯中间至少有 k − 1 k-1 k−1盏熄灭的灯
那么选出 p p p盏灯点亮,其余熄灭的灯形成 p + 1 p+1 p+1个连通块
我们只需要考虑让中间 p − 1 p-1 p−1个连通块大小大于等于 k − 1 k-1 k−1即可(这样两端也必定满足)
相当于我们先从中抽出 ( p − 1 ) ∗ ( k − 1 ) (p-1)*(k-1) (p−1)∗(k−1)盏灯,然后从剩下的灯任意选 p p p盏灯
方案数为 ( n − ( p − 1 ) ∗ ( k − 1 ) p ) {n-(p-1)*(k-1)\choose p} (pn−(p−1)∗(k−1))
可以这么想,假设现在只有 n − ( p − 1 ) ∗ ( k − 1 ) n-(p-1)*(k-1) n−(p−1)∗(k−1)盏灯,从中选择 p p p盏灯点亮
最后,在相邻两个被点亮的灯中间插入 k − 1 k-1 k−1盏熄灭的灯
于是,答案为
1 + ( n − ( p − 1 ) ∗ ( k − 1 ) p ) ∗ p ! n ∗ ( n − 1 ) . . . ∗ ( n − p + 1 ) 1+{n-(p-1)*(k-1)\choose p}*\frac{p!}{n*(n-1)...*(n-p+1)} 1+(pn−(p−1)∗(k−1))∗n∗(n−1)...∗(n−p+1)p!
注意不要忘了加 1 1 1,因为对于任何一个操作次数为 w w w的方案来说
我们只计算了它的前 w − 1 w-1 w−1步,没有计算最后那一步–终止的那一步
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6+10;
const int mod = 1e9+7;
#define int long long
int fac[maxn],n,m;
int quick(int x,int n)
{
int ans = 1;
for( ; n ; n>>=1,x=x*x%mod )
if( n&1 ) ans = ans*x%mod;
return ans;
}
int C(int n,int m)
{
if( n<=0 || m>n ) return 0ll;
return fac[n]*quick( fac[m]*fac[n-m]%mod,mod-2 )%mod;
}
signed main()
{
fac[0] = 1;
for(int i=1;i<=2000000;i++) fac[i] = fac[i-1]*i%mod;
int t; cin >> t;
while( t-- )
{
int n,k; scanf("%lld%lld",&n,&k);
int ans = 1;
for(int i=1;i<=n;i++)
{
int f = C(n-(i-1)*(k-1),i );
int p = fac[i]*quick( fac[n]*quick(fac[n-i],mod-2)%mod,mod-2 )%mod;
ans = ( ans+f*p%mod )%mod;
}
cout << ans << endl;
}
}