一、题目
二、解法
根据题意可以写出柿子(
A
k
A_k
Ak表示恰好做出
k
k
k道题的概率,
p
k
p_k
pk表示至少做出
k
k
k道题的概率):
∑
i
=
1
n
i
A
i
=
∑
i
=
1
n
p
i
\sum_{i=1}^niA_i=\sum_{i=1}^np_i
i=1∑niAi=i=1∑npi那么
p
i
p_i
pi怎么算呢?设
s
i
s_i
si为做前
i
i
i道题花的总时间(不算多花的),我们枚举多花的时间:
p
i
=
1
2
i
∑
j
=
0
min
(
n
,
T
−
s
i
)
C
i
j
p_i=\frac{1}{2^i}\sum_{j=0}^{\min(n,T-s_i)}C_i^j
pi=2i1j=0∑min(n,T−si)Cij但是暴力算
p
p
p显然会
T
T
T,我们考虑从上一位置的值快速推到这个位置来,设
f
(
n
,
m
)
=
∑
i
=
0
m
C
n
i
f(n,m)=\sum_{i=0}^{m}C_{n}^i
f(n,m)=∑i=0mCni,那么我们先考虑从
f
(
n
,
m
)
f(n,m)
f(n,m)推到
f
(
n
+
1
,
m
)
f(n+1,m)
f(n+1,m),那就推柿子呗:
f
(
n
+
1
,
m
)
=
∑
j
=
0
m
C
n
+
1
m
=
∑
j
=
0
m
C
n
m
+
C
n
m
−
1
=
2
f
(
n
,
m
)
−
C
n
m
f(n+1,m)=\sum_{j=0}^mC_{n+1}^m=\sum_{j=0}^mC_{n}^{m}+C_n^{m-1}=2f(n,m)-C_n^m
f(n+1,m)=j=0∑mCn+1m=j=0∑mCnm+Cnm−1=2f(n,m)−Cnm但是
m
m
m是会变的,没关系,他是单调递减的,那么直接暴力缩小上界均摊就可以达到
O
(
n
)
O(n)
O(n)
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005;
const int MOD = 1e9+7;
#define int long long
int read()
{
int num=0,flag=1;
char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,m,s,p,ans,a[M],fac[M],inv[M],pw[M];
void init(int n)
{
fac[0]=fac[1]=inv[0]=inv[1]=pw[0]=1;
for(int i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
for(int i=2;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
for(int i=1;i<=n;i++) pw[i]=pw[i-1]*500000004%MOD;
}
int C(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
signed main()
{
init(200000);
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=a[i-1]+read();
s=1;p=min(m,n);
for(int i=1;i<=n;i++)
{
if(m-a[i]<0) break;
s=(2*s-C(i-1,p))%MOD;
int np=min(m-a[i],n);
for(int j=p;j>np;j--)
s=(s-C(i,j))%MOD;
p=np;
ans=(ans+pw[i]*s)%MOD;
}
printf("%lld\n",(ans+MOD)%MOD);
}