1. 程式人生 > 其它 >【CF1523E】Crypto Lights

【CF1523E】Crypto Lights

題目

題目連結:https://codeforces.com/contest/1523/problem/E
\(n\) 個檯燈初始時都是暗的,每次等概率隨機一個暗檯燈將其點亮,若點亮後存在一個長度為 \(k\) 的連續段有大於一個檯燈被點亮則立刻停止,求期望點亮多少檯燈。答案對 \(10^9+7\) 取模。
\(2\leq n,k\leq 10^5\)

思路

\(f_i\) 表示選擇 \(i\) 次後結束的期望。那麼答案

\[=\sum^{n}_{i=1}f_i\times 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{\binom{n-(i-1)(k-1)}{i}}{\binom{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<<"\n";
	}
	return 0;
}