zoukankan      html  css  js  c++  java
  • CDOJ 1277 智商杯考试 每周一题 div2 二分+数学

    智商杯考试

    题目连接:

    http://acm.uestc.edu.cn/#/problem/show/1277

    Description

    你是一个挂科选手。

    你现在正在考试,你很方。

    你参加的考试叫做智商杯考试。

    这个考试很奇怪,考试一开始就把所有答案全部发给你了,但是并不告诉你答案和考试题目的对应关系,也就是说你根本不知道这个答案是哪个题目的。

    由于智商低的原因,你根本看不懂题目,所以从题目来推断答案究竟属于哪个题目是不可能的……

    但是别慌,你可以向监考老师提问嘛。

    智商杯的监考老师可是很人道的哦。

    他允许你一次性最多问m个答案的对应关系,他会告诉你这m个答案对应哪m道题,当然,他不会告诉你具体的对应关系。 比如:

    你可以问:1,2,3号答案对应哪些问题?
    监考老师说:对应3,7,9号问题。
    
    假设你比较蛋疼,你问:1,1,2,3号对应哪些问题?
    监考老师说:对应3,7,9号问题。
    

    现在问题来了,考试一共有n道问题,你每次提问最多提及m个答案,你需要最少问多少次呢?

    Input

    两个整数,1<=n,m<=1e9。

    Output

    输出一个整数,表示最少询问次数。

    Sample Input

    15 4

    Sample Output

    6

    Hint

    题意

    题解:

    每周一题 题解

    div2 智商杯考试

    首先这道题正面想比较麻烦。

    现在我们假设你知道了MaxN(m,k),即表示每次最多提及m个问题,我询问k次,最多知道多少个答案和题目对应的这个函数。

    那么我直接二分答案就好了

    只要解决了MaxN(m,k),那么原题就解决了。



    然后怎么去处理这个呢?

    我们这样想,我们一开始有n个k位二进制数,如果在第i轮回答中提及到了第j个问题的话,那么就令第j个二进制数的第i位为1。

    分析一下样例一,n = 4,k = 2,m = 2的情况:

    1 0 1 0
    1 1 0 0

    可以看到第一个问题的序列是11,第二个问题的序列是01,第三个是10,第四个是00

    由于这些二进制数都不一样,因此你能分辨出来。

    只要我们最后的n个k位二进制数全部不一样,你就能分辨。

    这个证明也很简单,如果有两个二进制数相同的话,那么肯定就可以交换着两个的问题的对应关系了。



    那么我们怎么去询问,才能使得1的个数尽量少,且问的问题多呢?

    贪心。

    C(k,0)个问题,我不提及;C(k,1)个问题,我就只提及一次;C(k,2)个问题,我提及两次........C(k,r)个问题,我提及r次。

    这样,最后的矩阵构造是这样的:

    0 1 0 0 1 1 ... 1
    0 0 1 0 1 0 ... 1
    0 0 0 1 0 1 ... 1
    . . . . . . ... 1
    . . . . . . ... 1
    0 0 0 0 0 0 ... 1

    然后最后再Check一下整个矩阵的1的个数是否超过k*m,。

    于是,这道题就解决了!



    有人问,为什么我们只用check总共1的个数是否超过k*m,而不去check每一行的1的个数是否超过m。

    其实你去check两个也是可以的,这个复杂度在本题也是可以接受的。

    但是为什么呢?

    证明如下:

    假设我们满足总共1的个数不超过k*m,但是存在某一些位的1的个数超过了m。

    那么必然存在一些位的1的个数少于m。

    我们选择其中一个超过m的位X,选择其中一个少于m的位Y。

    然后我们再从n个串中找到x个在X位为1,Y位为0的串,找到y个在X位中为0,Y位中为1的串。

    也是显然知道x>y。

    对于x个串,我们都将第X位置成0,Y位置成1,显然这x个串仍然都是各不相同的,且最多在y串中找到一个和他相同的。

    因为x>y,所以肯定能够找到一个串是在y串没有与之对应的,那么我们只要变这个串就好了。

    这样我们就得到了,X位置的1的总数-1,Y位置的1的总数+1了。

    这样总共1的个数是没有变的。

    所以只要满足总共1的个数不要超过k*m就好了。


    代码

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    int check(long long k)
    {
        if(k<=30&&n>(1<<k))return 0;
        long long t = 1LL*k*m;
        long long N = n;
        long long C = 1;
        long long ans = 0;
        for(int i=0;N>0;i++)
        {
            long long p = min(C,N);
            N-=p;
            ans+=p*i;
            C = C*(k-i)/(i+1);
            //cout<<i<<" "<<N<<" "<<p<<endl;
        }
        return ans<=t?1:0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int l = 0,r = n;
        int ans = 0;
        while(l<=r)
        {
            int mid = (l+r)/2;
            if(check(mid))r=mid-1,ans=mid;
            else l=mid+1;
        }
        cout<<ans<<endl;
    }
  • 相关阅读:
    window 7 安装Jmeter并配置https录制脚本
    windows 7安装Fiddler抓HTTPS请求的解决办法
    APP界面设计与页面布局的23条基本原则
    Charles 连接手机抓包出现Unknown,一直无法抓包的问题解决
    Charles 如何破解与连接手机进行抓包
    WeTest----如何查看Wetest生成测试报告?
    WeTest----如何使用WeTest进行App性能测试?
    兼容位图
    DeleteDC ReleaseDC DeleteObject之间的区别
    屏幕保存为位图
  • 原文地址:https://www.cnblogs.com/qscqesze/p/5219813.html
Copyright © 2011-2022 走看看