Codeforces 1271E - Common Number (二分,位运算)
思路
先用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;
}