膜 社论(egg drop)

题面

\(n\)\(m\) 个鸡蛋,从 \(k\) 楼及以上扔下去会碎,不能再测试 .

问至少需要扔几次确定 \(k\) .

\(n\le 10^{18}\)\(m\le 64\) .

题解

\(dp_{i,j}\) 表示 \(i\) 个鸡蛋丢 \(j\) 次能确定的最高楼层(其实是 OEIS A180975 / A131251) .

显然最后一个走第 \(k\) 步的时候,和上一步之间的空隙不能超过 \(dp_{i-1,j-k}+1\),不然中间无法判定 .

于是

\[\begin{aligned}dp_{i,j}&=\sum_{k=1}^{j}(dp_{i-1,j-k}+1)\\&=j+\sum_{k=0}^{j-1}dp_{i-1,k}\\&=dp_{i-1,j}+dp_{i-1,j-1}+1\end{aligned} \]

\(dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-1}+1\) .

我们转成一个二项式系数的形式:

\[dp_{i,j}=\sum_{p=0}^{j-1}\sum_{q=0}^{i-1}\dbinom pq \]

过程(artalter)

\(g_{i,j}=dp_{i+1,j}-dp_{i,j}\) .

\[\begin{aligned}g_{i,j}&=(1+dp_{i,j}+dp_{i,j-1})-(1+dp_{i-1,j}+dp_{i-1,j-1})\\&=dp_{i,j}+dp_{i,j-1}-dp_{i-1,j}-dp_{i-1,j-1}\\&=(dp_{i,j}-dp_{i-1,j})+(dp_{i,j-1}-dp_{i-1,j-1})\\&=g_{i-1,j}+g_{i-1,j-1}\end{aligned} \]

妈呀这不是组合数吗?

于是就可以轻易建立 \(dp\)\(g\) 的联系,从而推出式子了 .

随便推推

\[\begin{aligned}dp_{i,j}&=\sum_{p=0}^{j-1}\sum_{q=0}^{i-1}\dbinom pq\\&=\sum_{q=0}^{i-1}\sum_{p=0}^{j-1}\dbinom pq\\&=\sum_{q=0}^{i-1}\dbinom {j}{q+1}\end{aligned} \]

然后随便线性做了 .

外面套一个二分就完了 .

时间复杂度 \(O(m\log n)\) .


细节:\(dp\) 增长很快,大于 \(n\) 直接丢掉,用 double 注意浮点误差

代码

using namespace std;
typedef long long ll;
typedef __int128 i128;
const int N = 111;
const double LIM = 1e18;
ll n, k;
inline bool check(ll x)
{
	i128 now=1, ans=0;
	for (int l=0; l<k; l++)
	{
		if (1.0*now*(x-l)/(l+1) > LIM) return true;
		now = now*(x-l)/(l+1); ans += now;
		if (ans >= n) return true;
	} return ans >= n;
}
int main()
{
	int T; scanf("%d", &T);
	while (T--)
	{
		scanf("%lld%lld", &n, &k);
		ll l=0, r=n, ans=-1;
		while (l <= r)
		{
			ll mid = (l + r) >> 1;
			if (check(mid)){r = mid-1; ans = mid;}
			else l = mid+1;
		}
		printf("%lld\n", ans);
	}
	return 0;
}
posted @ 2022-02-12 19:17  yspm  阅读(180)  评论(0)    收藏  举报
😅​