题目
题目链接:https://codeforces.com/contest/1523/problem/E
有 (n) 个台灯初始时都是暗的,每次等概率随机一个暗台灯将其点亮,若点亮后存在一个长度为 (k) 的连续段有大于一个台灯被点亮则立刻停止,求期望点亮多少台灯。答案对 (10^9+7) 取模。
(2leq n,kleq 10^5)。
思路
记 (f_i) 表示选择 (i) 次后结束的期望。那么答案
[=sum^{n}_{i=1}f_i imes i=sum^{n}_{i=1}sum^{n}_{j=i}f_i
]
考虑如何求出 (g_i=sum^{n}_{j=i+1}f_i)。也就是至少选择 (i+1) 次后才停止的期望。
显然 (g_i) 等价于选择了 (i) 次依然没有停止的期望。那么问题转化为有 (n) 个灯,选择其中 (i) 个,两两之间距离大于等于 (k-1) 的期望。
我们可以把每一个灯看做一个长度为 (k) 的线段,那么就是选择 (i) 个长度为 (k) 的区间互相不交。注意最后一个区间的右端点是可以超过 (n) 的。
那么我们直接把每一个区间的后 (k-1) 个元素删掉,问题转化为在 (n-(i-1)(k-1)) 个灯中选择 (i) 个的方案数。这个东西和刚刚的模型是等价的。假设现在的模型中选择了 (i,j(i<j)) 为相邻两盏灯,那么原模型中这两盏灯之间的距离就是 (j-i+k-1)。
所以有
[g_i=frac{inom{n-(i-1)(k-1)}{i}}{inom{n}{i}}
]
答案就是 (sum^{n-1}_{i=0}g_i)。
时间复杂度 (O(Qn))。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,MOD=1e9+7;
int Q,n,k,ans,fac[N],inv[N];
ll fpow(ll x,ll k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%MOD)
if (k&1) ans=ans*x%MOD;
return ans;
}
ll C(ll n,int m)
{
if (n<m) return 0;
return 1LL*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
ll invC(int n,int m)
{
if (n<m) return 0;
return 1LL*inv[n]*fac[m]%MOD*fac[n-m]%MOD;
}
int main()
{
fac[0]=inv[0]=1;
for (int i=1;i<N;i++)
fac[i]=1LL*fac[i-1]*i%MOD;
inv[N-1]=fpow(fac[N-1],MOD-2);
for (int i=N-2;i>=1;i--)
inv[i]=1LL*inv[i+1]*(i+1)%MOD;
scanf("%d",&Q);
while (Q--)
{
scanf("%d%d",&n,&k);
ans=1;
for (int i=1;i<n;i++)
ans=(ans+C(n-1LL*(i-1)*(k-1),i)*invC(n,i))%MOD;
cout<<ans<<"
";
}
return 0;
}