二进制题目的考法总结

二进制和gcd结合使用

https://codeforces.com/gym/104828/problem/I
题目是一个交互题,大意是让你猜出两个数x和y,每次询问要给出两个数字a,b,然后会反馈gcd(x+a,y+b)的值.
这道题需要按位思考,两个数x和y的最低位无非就四种情况(0,0),(1,0),(0,1),(1,1).
确认两个数的每一位需要三次询问,第一次询问给它们每人加上一个2^0,如果是(1,1)的情况,那么双双加上一个2^0以后变成了(0,0)经过gcd处理以后得到的结果一定是2^1的倍数,%2^1以后一定是0,对于其它情况按照相同的操作处理后得出来的结果一定不是0.所以可以确定x和y的最低位是(1,1),那么其它情况该如何处理呢,(1,0)的情况,我们可以尝试询问(x,y+2^0),(0,1)的情况,我们可以尝试询问(x+2^0,y).当然这是处理低位的情况,高位该如何处理呢?
我们把每一位得到的结果存在两个数x1,y1里,比如说已经询问到2^m(第m位)了,我们让x+2^m-x1,可以很轻松的吧0到m-1位全部置为0,然后第m位会多加上一个1.同理y也进行同样的操作,把前m-1位置为0,第m位+1,然后进行gcd,此时x和y的第m位如果都是1,那么经过归零操作后变成了0,此时就不需要再加2^m来试探了,直接gcd(x,y)就可以确定该位原本是否都为1.
代码如下
 

ll ask(ll a, ll b) {
    std::cout << "? " << a << " " << b <<"\n"<< std::endl;
    ll k;
    std::cin >> k;
    return k;
}
void ans(ll a, ll b) {
    std::cout << "! " << a << " " << b <<"\n"<< std::endl;
}
void solve() {
    ll x1 = 0, x2 = 0, a1, a2, a3;
    for (ll i = 0; i < 60; i++) {
        ll m = (1LL << i);
        a1 = ask(m - x1, m - x2) % (m * 2);//1,1
        a2 = ask(m - x1 + m, m - x2) % (m * 2);//0,1
        a3 = ask(m - x1, m - x2 + m) % (m * 2);
        if (a1 == 0) {
            x1 = x1 + (1LL << i);
            x2 = x2 + (1LL << i);
        }
        else if (a2 == 0) {
            x2 = x2 + (1LL << i);
        }
        else if (a3 == 0) {
            x1 = x1 + (1LL << i);
        }
    }
    ans(x1, x2);
}

二进制与排列 

​​​​​​https://codeforces.cm/contest/2040/problem/C​​​​​​

通过打表找规律可以发现S(p)最大值的排列都满足排列的顺序先单调递增,后单调递减.我们还可以很容易的推断出长度为n的排列,满足S(p)最大值的个数是2^(n-1)个.
对于n,总共有2^(n-1)次方个排列,我们从0开始编号.
那么所有的排列是不是变成了[0,2^(n-1)-1]
第k个变成了第k-1个.
我们也知道,将数字小的放左边,它会降低字典序的排名,把数字小的放右边它会增加字典序的排名
这样我们可以确定,当剩下数字可以组合而成的排列数大于等于k时,我们把这个数字放在右边,从而跳过2^(n-1)个排列,以缩短左边界,同理>k的时候,我们则不跳过2^(n-1)个排列,让更小的去凑k.

void solve() {
    int n;
    ll k;
    std::cin >> n >> k;
    k--;
    if (n <= 60 && k >= (1LL << (n - 1))) {
        std::cout << -1 << "\n";
        return;
    }
    std::vector<int>p(n);
    int l = 0, r = n - 1;
    for (int i = 1; i < n; i++) {
        if (n - 1 - i > 60 || k < (1LL << (n - 1 - i))) {
            p[l++] = i;
        }
        else {
            p[r--] = i;
            k -= (1LL << (n - 1 - i));
        }
    }
    p[l] = n;
    for (int i = 0; i < n; i++) {
        std::cout << p[i] << " \n"[i == n - 1];
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值