参考:http://www.cnblogs.com/qscqesze/p/5219813.html
题目:
你是一个挂科选手。
你现在正在考试,你很方。
你参加的考试叫做智商杯考试。
这个考试很奇怪,考试一开始就把所有答案全部发给你了,但是并不告诉你答案和考试题目的对应关系,也就是说你根本不知道这个答案是哪个题目的。
由于智商低的原因,你根本看不懂题目,所以从题目来推断答案究竟属于哪个题目是不可能的……
但是别慌,你可以向监考老师提问嘛。
智商杯的监考老师可是很人道的哦。
他允许你一次性最多问m个答案的对应关系,他会告诉你这m个答案对应哪m道题,当然,他不会告诉你具体的对应关系。 比如:
你可以问:1,2,3号答案对应哪些问题?
监考老师说:对应3,7,9号问题。
假设你比较蛋疼,你问:1,1,2,3号对应哪些问题?
监考老师说:对应3,7,9号问题。
现在问题来了,考试一共有n道问题,你每次提问最多提及m个答案,你需要最少问多少次呢?
Standard Input
两个整数,1<=n,m<=1e9。
Standard Output
输出一个整数,表示最少询问次数。
Samples
Input | Output |
---|---|
4 2 | 2 |
7 3 | 3 |
15 4 | 6 |
Note
样例解释:
你:1,2号答案对应哪些题目呢?
监考老师:对应3,4号题目。
你:1,3号答案对应哪些题目呢?
监考老师:对应2,4号题目。
你想了想:由于我问了两遍,都出现了4,所以4号题目肯定对应1号答案;再从第一个询问中得知,3号题目对应2号答案;第二个询问知道,2号题目对应3号答案;哦,那么剩下的4号答案就对应1号题目了!
这样,你就智商得到了升华。
完美。
by qscqesze
Problem ID | 1277 |
Time Limit | 1000 ms |
Memory Limit | 64 MiB |
Output Limit | 64 MiB |
Source | 每周一题 div2 |
题解:
需要了解的重要一点。我们要求的是至少询问几个问题能得到这些答案。
利用二进制表示答案的种数(类似于喂药喂几只小白鼠判断毒药)。
假设有n个药,把药变为二进制,10=1010,表示喂给第一只小白鼠和第三只小白鼠。从而可以单独判断出这个药。
10只小白鼠可以判断出1024种药。
这里同理,每道题用二进制表示从而可以单独判断,位置上为1表示要问这个问题。只需要二分答案ans,通过得到满足的k再判断最少用多少1,并且比较一下1会不会超过k*m.
我们已经知道的是每一列也就是1个问题不能超过m,假设存在1的个数等于k*m,但有一列超过m了。
我们得到1的总数很简单。先从C(k,0)开始,C(k,1)贪心加1上去,能保证最小,如果取到C(k,i),并且只取中间一部分。
比如C(4,2):必然先取0011,1100保证每一列最多加1,但是C(4,3)0111 1110有两位会多加上1,但是之后肯定会通过1101,1011保证每一步每一列1的差距最多是1。如果有一列大于m,必然有一列小于m。假设一个是m+1,一个就是m-1.这个差距是不可能达到。所以简单的判断和k*m的大小比较即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(a,b) for(int i=a;i<=b;i++)
#define red(a,b) for(int i=a;i>=b;i--)
#define ULL unsigned long long
#define LL long long
LL pow(int base,int r)
{
LL ans=1;
while(r){
if(r&1)
{
ans*=base;
}
r>>=1;
base*=base;
}
return ans;
}
LL n,m;
int check(long long k)//检验k个问题能否满足出现n个二进制数,满足的话能否限制每一列的和都小于等于m
{
if(pow(2,k)<n)return false;
else
{
LL inf = k*m;
LL C=1;
LL N=n;
LL m;
LL ans=0;
LL i=0;
// cout<<k<<endl;
while(N)
{
m=min(C,N);//防止减成负的了。
N-=m;
ans+=m*i;
i++;
C=C*(k-i+1)/i;
// cout<<C<<endl;
}
if(ans>inf)return false;
else return true;
}
}
int main()
{
cin>>n>>m;
LL ans=0;
LL r=n,l=1;
while(l<=r)
{
LL mid=(l+r)/2;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans<<endl;
system("pause");
}