• CF1665 DV2 D. GCD Guess


    https://codeforces.com/contest/1665/problem/D

        这里有个正整数 x (1 <= x <= 1e9) 让你猜
        你可以通过询问来获取信息
        你可以选择两个不同的正整数 a, b (1 <= a, b <= 2e9)来进行一次询问
        在一次询问后 你会获得 gcd(x + a, x + b) 的值。
        定义: gcd(a, b) => a 和 b 的最大公约数
        现在请你进行最多30次询问来猜出这个数
    

    首先看题目的限制,最多猜30次,而要猜的数是在 1 ~ 1e9 的范围,那么很自然的想到需要logN的算法。
    我首先考虑的是二分答案,然而并没有什么卵用,因为没有办法从题目给定的操作中获取有用的信息。
    再思考还有什么是满足logN的算法,再看题目,要猜的数最大是1e9,询问最多30次,让我们看下2^30有多大: 2^30 = 1073741824 > 1e9,所以x的二进制表示最多30位((2^30) - 1 = 1073741823 > 1e9),于是又很自然地联想到二进制,顺着这个思路往下,这样每一次询问我们都应该想办法获取x的二进制表示上的其中一位值是多少。接下来思考如何通过gcd来获取二进制的其中一位。
    对于101101,假设我们要通过gcd获取它的从低到高(从右到左)第3位该怎么办?既然是二进制,那就从二进制的方向去考虑。

    //数字后面带 B 代表这是一个二进制表示的数
    a = 101100 B = 11 * (2 ^ 2)
    b = 110100 B = 13 * (2 ^ 2)
    gcd(a, b) = 2 ^ 2 = 100 B
    //                ~~^~~第三位为1
    

    发现什么了吗,对于上面那个例子,我们可以构造出两个数,使得它二进制表示的第3位右边全为0,而第3位的左边一位不同(这样可以保证gcd取到的数不受第3位(二进制表示下)右边的数影响),来判断它的第3位是0还是1
    那么如何将它右边的数字位置0呢。
    考虑取1011 B 第4位的值之前进行的置零操作

    //这里有 1011 B, 考虑加上某个数(别忘了题目要求)将其最高位(第4位)的右边置零
    1011 B + (1000 B - 011 B) = 10000 B
                                 1011 B
    //因为1011 B最高位是第4位所以加上的是 1000 B - 前3位
    //同理 如果最高位是第3位 就加上 100 B - 前2位
    

    我们发现这样取到的第4位与原来的值相反(可以自己证明一下,一定是相反的),那么我们在处理的时候只需简单取反即可。
    这样从低到高处理,就可以很方便地得出答案了。
    接下来就是快乐代码时间

    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    ll qry(ll a, ll b){
        printf("? %lld %lld\n", a, b);
        fflush(stdout);
        ll x;
        scanf("%lld", &x);
        return x;
    }
    int main(){
        int T;
        for(scanf("%d", &T); T--;){
            ll res = 0;
            for(int i = 1; i <= 30; i++){
                ll x = (1ll << (i - 1)) - res;            
                ll a = (1ll << i) + x;
                ll b = x;
                ll t = qry(a, b);
                res |= (!(t >> (i - 1) & 1)) << (i - 1);
            }
            printf("! %lld\n", res);
            fflush(stdout);
        }
    }
    
  • 相关阅读:
    单链表反转
    C++面试题
    堆排序
    1链表:回文链表(leetcode 234)
    深信服社招linux岗面试记录
    腾讯后台开发社招记录(电话面试)
    小米社招ATE岗位记录
    诺基亚社招C++面试记录
    腾讯后台开发社招面试记录
    makefile笔记
  • 原文地址:https://www.cnblogs.com/slwang/p/16331197.html
Copyright © 2020-2023  润新知