F. Crossword Expert(组合数学,前缀期望)

题意:给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[i1]求出解决问题个数等于i的个数,并求其贡献。
至于求 f [ i ] f[i] f[i],可以这样考虑,对于前序 i − 1 i-1 i1个问题,全部取 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+1remain(表示要从前序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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值