【codeforces 431D】Random Task

【题目链接】:http://codeforces.com/problemset/problem/431/D

【题意】

给你一个数字m和数字k;
让你找一个数字x;
使得x+1..2*x这个区间范围内的数,它们的二进制形式里面,1的个数恰好为k个的数字恰好有m个;

【题解】

首先需要知道x+1..2*x这里面二进制1的个数的为k的数字的个数是随着x单调不下降的;
比如

n = x=>x+1,x+2...2*x
n = x+1=>  x+2...2*x,2*x+1,2*x+2


这里x+1的1的个数和2*x+2的1的个数是一样的;
因为乘2的话,会让所有的1整体左移;但1的个数不变;
这样
x+1,x+2…2*x
x+2…2*x,2*x+2
它们俩含k个1的个数是相同的;
但是n=x+1的时候还有一个2*x+1;这个数字可能是含k个1的,所以说是单调不下降的;
知道单调性之后;
就二分n吧.
然后求某个范围(L,R]里面二进制有k个的1的数的个数;
可以先求出1..L和1..R这两个区间范围内的有k个1的数的个数;
然后相减就是这个区间的了;
而求1..x中有k个1的数的个数;
是个计数的问题了;
具体的,把x的二进制形式中的某个1给变成0;
(计数完之后还原就是了);
然后假设这是从右往左数第i位;
则在i-1个数字中选择剩下的所需的1的个数就好(k减去这一位左边的1的个数);
(因为第i位是1,所以变成0后,后面可以出现任意数量的1);
如果x也是k个1别忘了算上;

【Number Of WA

0

【完整代码】

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ms(x,y) memset(x,y,sizeof x)
#define Open() freopen("F:\\rush.txt","r",stdin)
#define Close() ios::sync_with_stdio(0),cin.tie(0)

typedef pair<int,int> pii;
typedef pair<LL,LL> pll;

const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int N = 66;

LL m,k;
LL c[N][N],t[N];

void pre(){
    t[0] = 1;
    rep1(i,1,63){
        t[i] = t[i-1]*2;
    }
    rep1(i,0,65)
        c[i][0] = c[i][i] = 1;
    rep1(i,1,65){
        rep1(j,1,i-1){
            c[i][j] = c[i-1][j]+c[i-1][j-1];
        }
    }
}

LL get_sum(LL x){
    int cur = 0;
    LL temp = 0;
    for (LL i = 63;i >= 0;i--)
    if (x&t[i]){
        if (k-cur>=0)
            temp += c[i][k-cur];
        cur++;
    }
    if (cur==k) temp++;
    return temp;
}

LL check(LL n){
    LL temp1 = get_sum(2*n);
    LL temp2 = get_sum(n);
    temp1-=temp2;
    return temp1;
}

int main(){
    //Open();
    Close();//scanf,puts,printf not use
    //init??????
    pre();
    cin >> m >> k;
    LL l = 1,r = (LL) 1e18,ans = 0;
    while (l <= r){
        LL mid = (l+r)>>1;
        LL temp = check(mid);
        if (temp>=m){
            ans = mid;
            r = mid - 1;
        }else {
            l = mid + 1;
        }
    }
    cout << ans << endl;
    return 0;
}
posted @ 2017-10-04 18:44  AWCXV  阅读(150)  评论(0编辑  收藏  举报