P1277 智商杯考试

参考: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

InputOutput
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 ID1277
Time Limit1000 ms
Memory Limit64 MiB
Output Limit64 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");
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值