题目描述
题目大意
有n个物品,拿走每个物品需要ti或ti+1的时间(二者等概率发生),只能按顺序拿,求在T时间内期望拿走的物品个数。
前言
由于是第一次爆肝CF,网速+手速导致了E题没写完,F题没想出来
其实前6题还是很清真的
题解
显然分别考虑每个物品的期望贡献,用sum[i]表示t[i]的前缀和
一个物品产生的贡献为1*期望被拿到的概率
而概率=
∑
j
=
1
m
i
n
(
T
−
s
u
m
[
i
]
,
i
)
C
i
j
2
i
\frac{\sum_{j=1}^{min(T-sum[i],i)}{C_{i}^{j}}}{2^i}
2i∑j=1min(T−sum[i],i)Cij,相当于先各自减去ti,就变成了在i个数中选小于等于min(T-sum[i],i)个数的方案和除以总方案
问题是怎么求组合数的前缀和
前缀和
后来看到网上有用莫队来求组合数前缀和的,但这样做就比较naive了
因为题目中的min(T-sum[i],i)肯定是单调不增的,所以每次只需要快速求出第i行中min(T-sum[i-1],i)的前缀和,然后减掉前面min(T-sum[i-1],i)-min(T-sum[i],i)个数就可以了(就是从i-1的位置减到i的位置)
计算前缀和:
①T-sum[i-1]≥i
那么前缀和=i-1行前缀和*2
②T-sum[i-1]<i
那么前缀和=i-1行前缀和*2-C(i-1,T-sum[i-1])
(把0~T-sum[i-1]分别向左/右加一次,(i-1,T-sum[i-1])位置多向右加了要减去)
code
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define mod 1000000007
#define Mod 1000000005
#define two 500000004
using namespace std;
long long jc[200001];
long long Jc[200001];
long long w[200001];
int t[200001];
int n,i,j,k,l;
long long T,s,ans;
long long qpower(long long a,int b)
{
long long ans=1;
while (b)
{
if (b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
long long C(int n,int m)
{
return jc[n]*Jc[m]%mod*Jc[n-m]%mod;
}
int main()
{
// freopen("f.in","r",stdin);
scanf("%d%I64d",&n,&T);
fo(i,1,n)
scanf("%d",&t[i]);
jc[0]=1;jc[1]=1;
Jc[0]=1;Jc[1]=1;
w[1]=1;
fo(i,2,n)
{
w[i]=mod-(long long)(mod/i)*w[mod%i]%mod;
jc[i]=jc[i-1]*i%mod;
Jc[i]=Jc[i-1]*w[i]%mod;
}
s=1;
fo(i,1,n)
{
s=s*2%mod;
if (T<=i-1)
s-=C(i-1,T);
T-=t[i];
if (T<0)
break;
if (T<i)
{
fd(j,min(T+t[i],i),T+1)
s=(s-C(i,j))%mod;
if (s<0)
s+=mod;
ans=(ans+s*qpower(two,i)%mod)%mod;
}
else
++ans;
}
printf("%I64d\n",ans);
}