E. Common Number (思维 + 规律)

传送门

题意: 有一个函数 f(x) 是这样定义的

  然后我们定义路径 path(x),为令 x = f(x) 直到   x == 1;

  例如: path(15)=[15,14,7,6,3,2,1],  path(32)=[32,16,8,4,2,1].

  现在给你 n 和 k。 问你在 1 ~ n 的所有数的路径中出现至少 k 次的最大的数是多少。

 

解: 考虑对于每个 x 在路径出现的次数:count(x);

   若 x 为偶数,那么,路径中有 x 的数,有 x + 1, 2x,  2x+1, 2x+2, 2x+3, 4x, 4x+1, 4x+2,....4x+7;

   若 x 为奇数,那么,路径中有 x 的数,有 2x, 2x + 1, 4x, 4x + 1, 4x + 2, 4x + 3, 8x,......

  那么对于每个 x 的 count(x) 我们可以在 logn 时间里求得。

  这里还有一个性质, count(x) >= count(x + 2);

  显然, x 和 x + 2 的奇偶性是一样的,那么,对于同样的上限, 若存在路径中包含 x + 2 的数 a;

  那么肯定存在一个更小的数 b ,路径中 包含 x;

  然后,我们就可以二分求得了。

#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
using namespace std;

const int mod = 1e9 + 7;

bool check(LL x, LL k, LL n) {
    LL l = x, r = x + 1LL;
    if(x & 1) r--;
    LL res = 0LL;
    while(l <= n) {
        res = res + min(n, r) - l + 1;
        l = l << 1LL;
        r = (r << 1LL) + 1LL;
    }
    return res >= k;
}

int main() {
    LL n, k;
    scanf("%I64d %I64d", &n, &k);
    LL l = 1LL, r = n + 1LL, ans = 1LL;
    while(l <= r) {
        LL mid = (l + r) >> 1LL;
        if(check(mid << 1LL, k, n)) {
            l = mid + 1LL;
            ans = max(ans, mid << 1LL);
        }
        else if(check((mid << 1LL) - 1LL, k, n)) {
            l = mid + 1LL;
            ans = max(ans, (mid << 1LL) - 1LL);
        }
        else r = mid - 1LL;
    }
    printf("%I64d\n", ans);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Willems/p/12186706.html