大意
给你(N)个事件,解决每个事件所需的时间有(1/2)的概率为(t[i]),(1/2)的概率为((t[i]+1)),给你总时间(T),在(T)时间内按顺序解决事件,求能解决的事件的期望个数。
(答案对(10^9+7)取模)
((Nle 2cdot 10^5,1le t[i]le 10^9,1le Tle 2cdot 10^{14}))
思路
考虑如何求期望:
我们设(P[i])表示第(i)件物品能被做完的概率。
则有$$Ans=sum_{i=1}^{N}P[i]$$
则问题就转化为如何求(P[i])。
我们设(Sum[i])表示前(i)件事的最小时间和,即(Sum[i]=sum_{i=1}^{N}t[i])。
①:对于(Sum[i]+ile T)的情况:
则第(i)件事一定会被做完,故(P[i]=1)。
②:对于(Sum[i]le T<Sum[i]+i)的情况:
我们设(Dp[i][j])表示前(i)件事有(j)件事时间多做了(1)个单位的概率,即多做了(j)个时间单位,
则对于每个(Dp[i][j]),若有(Sum[i]+j<=T),则可以对(P[i])产生(Dp[i][j])的贡献。
考虑如何求(Dp[i][j]):
将这些事件按是否多做(1)个时间单位分类,
若完成时间为(t[i]),则类型为(0),
若完成时间为(t[i]+1),则类型为(1),
则可以将这些事件的状态表示为一个(01)串。
则总状态数就为(2^i),从(i)个数中选(j)个让其状态为(1)的个数就为(C(i,j)),
则(Dp[i][j]=frac{C(i,j)}{2^i})。
对于每个(P[i]),
我们倘若每次都去枚举有哪些(j)是可以满足(Sum[i]+j<=T)的话,很明显会超时。
则考虑如何从上一次((P[i-1]))所需的状态数转到(P[i])的状态数。
(注:第一次进入情况②的时候可以暴力找到状态)
考虑如何快速地从(P[i-1])转移到(P[i])的状态:
我们设上一次需要的(C)是从(C(i-1,0))到(C(i-1,Sum_K)),
设上一次的(P[i-1]=frac{Sum_N}{2^{i-1}}),则有(Sum_N=sum_{j=0}^{Sum_K}C(i-1,j))。
根据$$C(i,j)=C(i-1,j)+C(i-1,j-1)$$
则有$$sum_{j=1}{Sum_K}C(i,j)=sum_{j=1}{Sum_K}(C(i-1,j)+C(i-1,j-1))$$
则$$sum_{j=1}{Sum_K}C(i,j)=(2*sum_{j=0}{Sum_K}C(i-1,j))-C(i-1,0)-C(i-1,Sum_K)$$
然后,对于这次的(Sum_N)来说,
(Sum_N=sum_{j=1}^{Sum_K}C(i,j)+C(i,0)-sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j))
(Sum_N=(2*sum_{j=0}^{Sum_K}C(i-1,j))-C(i-1,Sum_K)-sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j))
(Sum_N=Sum_N*2-C(i-1,Sum_K)-sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j))
则这一次的(Sum_N)就可以从上一次的(Sum_N)转移过来。
显然这一次的(Sum_K=T-Sum[i])。
则(Sum_K)会随着(i)的增大而减小,
而进入情况②的条件是:(Sum[i]<=T<Sum[i]+i),
即求解所有的(Sum_N)的时间复杂度总计(O(N))。
③:(Sum[i]>T)时
则事件(i)一定不会被做完,即(P[i]=0)。
综上,(Ans)得解。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int ON=200000;
const int MAXN=200005;
const long long ONE=1;
const int MOD=1000000007;
int N,t[MAXN];
long long F[MAXN];
long long T,Sum[MAXN],Ans;
long long f[MAXN]={1},fe[MAXN]={1};
long long O[MAXN]={1},Oe[MAXN]={1};
long long Sum_N,Sum_K;
LL quick_Pow(LL x,LL y){
if(y==0)return 1;
if(y==1)return x;
if(y%2)return (x*quick_Pow((x*x)%MOD,y/2))%MOD;
return quick_Pow((x*x)%MOD,y/2);
}
void Prepare(){
for(int i=1;i<=ON;i++){
f[i]=(f[i-1]*i)%MOD;
fe[i]=quick_Pow(f[i],MOD-2);
O[i]=(O[i-1]*2)%MOD;
Oe[i]=quick_Pow(O[i],MOD-2);
}
}
long long C(long long x,long long y){
if(y>x)return 0;
return (f[x]*((fe[y]*fe[x-y])%MOD))%MOD;
}
long long work(long long n,long long k){
if(Sum_K==0){
for(int i=0;i<=k;i++)
Sum_N=(Sum_N+C(n,i))%MOD;
}else{
Sum_N=(Sum_N*2-C(n-1,Sum_K)+MOD)%MOD;
for(int i=Sum_K;i>k;i--)
Sum_N=(Sum_N-C(n,i)+MOD)%MOD;
}
Sum_K=k;
return Sum_N;
}
int main(){
Prepare();
scanf("%d%lld",&N,&T);
for(int i=1;i<=N;i++){
scanf("%d",&t[i]);
Sum[i]=Sum[i-1]+t[i];
}
for(int i=1;i<=N;i++){
if(Sum[i]>T)break;
if(Sum[i]+i<=T){
F[i]=1;
continue;
}
F[i]=(work(i,T-Sum[i])*Oe[i])%MOD;
}
for(int i=1;i<=N;i++)
Ans=(Ans+F[i])%MOD;
printf("%lld
",Ans);
}