题意:给n个问题,给出每个问题的解决时间ti,对于每个问题需要ti或ti+1个单位时间去解决,且要按给出的顺序解决,求在T时间内,对于解决问题个数的期望。
思路:先假设每个问题时间消耗都是
t
i
+
1
t_i+1
ti+1,求出
f
[
i
]
f[i]
f[i](解决问题个数大于等于i的概率),然后利用
f
[
i
]
−
f
[
i
−
1
]
f[i]-f[i-1]
f[i]−f[i−1]求出解决问题个数等于i的个数,并求其贡献。
至于求
f
[
i
]
f[i]
f[i],可以这样考虑,对于前序
i
−
1
i-1
i−1个问题,全部取
t
i
+
1
t_i+1
ti+1的时间消耗,然后考虑时间剩余量
r
e
m
a
i
n
remain
remain,设
g
a
p
=
t
i
+
1
−
r
e
m
a
i
n
gap=t_i+1-remain
gap=ti+1−remain(表示要从前序i个中选取gap个问题,将其时间消耗定为
t
i
t_i
ti),然后以此类推,公式很好推。
其中有个组合数前缀的优化需要记一下。
const int N = 2e5 + 5;
ll fa[N + 1], infa[N + 1];
int a[N];
ll n, t;
void x_x()
{
fa[0] = infa[0] = 1;
f(i, 1, N)fa[i] = fa[i - 1] * i%mod;
infa[N] = inv(fa[N], mod);
ff(i, N - 1, 1)infa[i] = infa[i + 1] * (i + 1) % mod;
}
ll C(ll a, ll b)
{
if (a < b)return 0;
return fa[a] * infa[a - b] % mod*infa[b] % mod;
}
ll pren = 0,prebot;
ll work(ll bot,int have)
{
//C(n,m)+C(n,m+1)=C(n+1,m+1);
if (!pren)
{
f(i,0,bot-1) pren= (pren + C(have, i)) % mod;
}
else
{
//f(n,m)=f(n-1,m)*2-C(m,n-1);继承上层,减少重复计算
pren = (2 * pren - C(have - 1, prebot) + mod) % mod;
f(i, prebot+1, bot-1)
pren = (pren+C(have, i)) % mod;
}
prebot = bot-1;
return pren;
}
int main()
{
//freopen("in.txt", "r", stdin);
x_x();
while (cin >> n >> t)
{
f(i, 1, n)scanf("%d", &a[i]);
int last = 0;
ll sum = 0, ans = 0;
f(i, 1, n)
{
if (sum + a[i] + 1 <= t)sum += a[i] + 1,last=i;
else break;
}
ll re = t - sum;
ll now = 0;
ll preexp = 1;
ll pre = inv(quickmod(2, last + 1, mod), mod);
ll pre2 = quickmod(2, last + 1,mod);
ll INV = inv(2, mod);
f(i, last + 1, n)
{
now+=a[i] + 1;
ll need = now - re;//需要从前缀中选几个
if (i < need)
{
ans = (ans + (i - 1)*preexp % mod) % mod;
break;
}
else
{
ll tt = work(need, i);
ll tm = (pre2-tt+mod)%mod;
tm = tm * pre%mod;
ans = (ans+(i - 1)*(preexp-tm+mod) % mod)%mod;
preexp = tm;
if(i==n)ans = (ans + i*preexp % mod) % mod;
}
pre = pre * INV%mod;
pre2 = pre2 * 2 % mod;
}
cout << ans << endl;
}
return 0;
}