Description
一栋高度为n层的楼,你有m个棋子,在第x层以上扔棋子会碎。
问对于x=0~n,最坏情况下需要试多少次才可以试出这个x。
T<=50000,1 <= N <= 10^18, 1 <= M <= 64
Solution
是不是长得有点眼熟?
相信大家都在小学奥数书上见过这种题。
不过搬到OI上来似乎就没有那么好做了。
最优策略很难想啊。。。
那我们不妨换个角度。
直接求次数很难,那么我们可以设F[i,j]表示扔i次,有j个棋子,最高能试出多少层楼。
也就是保证1~F[i,]j都可以试出来。
那么我们想一想现在的最优策略。
我们考虑先扔在某一层楼。
如果棋子碎了,那么就表示x在它以下,那么现在和上面这些楼层都是无关的,少了一个棋子,所以往下最多试出F[i-1,j-1]层。
如果棋子没碎,那么就表示x在它以上,同样和下面也是无关的,也就是往上最多试出F[i-1,j]层。
所以F[i,j]=F[i-1,j-1]+F[i-1,j]+1
如果处理出来,然后对于每个询问,二分一下就好了。
显然,这东西是指数级别增长的,真正有用的状态数不多。
但是当m=1和m=2时还是有点多的,可以特判一下。
懒得特判写vector,跑的好慢啊!!!
Code
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define pb push_back
using namespace std;
typedef long long ll;
typedef vector<ll> vec;
const ll Mx=1e18;
const int N=2*1e6;
ll m;
vec f[65];
int n,ty;
int main() {
fo(i,1,N) f[0].pb(0);
fo(j,1,64) {
f[j].pb(0);
fo(i,1,N) {
f[j].pb(f[j][i-1]+f[j-1][i-1]+1);
if (f[j][i]>Mx) break;
}
}
for(scanf("%d",&ty);ty;ty--) {
scanf("%lld%d",&m,&n);
if (n==1) printf("%lld\n",m);
else if (n==2) {
ll x=sqrt(2*m);
if (x*(x+1)<2*m) x++;
printf("%lld\n",x);
} else {
int x=upper_bound(f[n].begin(),f[n].end(),m)-f[n].begin();
if (f[n][x-1]==m) x--;
printf("%d\n",x);
}
}
}