Codeforces 1271E

Description

思路

先用dp打表发现x出现个数是按奇数和偶数分别降序。因此可以分开奇数偶数列分开二分。
事实上如果x是i的二进制前缀,那么x就可以通过f(i)获得,即x在i的path里。
所以x的出现个数就是小于等于n中以x为二进制前缀的个数。x为偶数时还要加上x+1的个数。细节详见代码。
注意!!&的优先级小于==!被这个坑了。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
const int M = 3e5+10;
#define inf 0x3f3f3f3f
typedef pair<int, int> PII;

ll count(ll x, ll n) { //不大于n的数中以x为二进制前缀的个数
    ll two = 1;
    ll res = 0;
    while(x <= n) {
        if((n & (~(two - 1))) == x) res += n - x + 1;
        else res += two;
        x <<= 1;
        two <<= 1;
    }
    return res;
}

ll work(ll x, ll n) { //计算x的出现次数
    if(x % 2 == 0) return count(x, n) + count(x + 1, n);
    return count(x, n); 
}

ll solve(ll mx, ll n, ll k, ll d) {
    ll l = 1, r = mx;
    while(l <= r) {
        ll mid = (l + r) / 2;
        if(work(2 * mid + d, n) >= k) l = mid + 1;
        else r = mid - 1;
    }
    return 2 * r + d;
}

 
int main() {
    ios::sync_with_stdio(false);
    ll n, k;
    cin >> n >> k;
    ll odn, evn;
    if(n % 2 == 0) odn = evn = n / 2;
    else odn = n / 2 + 1, evn = n / 2;
    ll ans = max(solve(odn, n, k, -1), solve(evn, n, k, 0));
    cout << ans << endl;
}
原文地址:https://www.cnblogs.com/limil/p/12619330.html