codeforces 431 D. Random Task 组合数学

题意:

给定m,k

0 <= m <= 10^18 ,1 <= k <= 64

求一个数n,满足n+1,n+2,...n+n这n个数中,刚好有m个数的2进制表示法刚好有k个1

保证答案在10^18内

思路:

显然,

对于x,如果x+1,x+2,...,x+x有y个数有k个1

对于x+1,则x+2,x+3,...,x+x+2有k个1的数的个数 >= y

满足单调性,考虑二分:

L = m,r = 10^18

那么问题变为:给定一个数x,x+1,x+2,...,x+x中刚好有k个1的数有多少个

为了方便表达,假设x是2进制数:

则出现k个1的区间有:

[x,11...111],[10...000,2x]

即按照位数分成2段计算

函数go(LL x,int k)作用:求[x,1111111]这个区间中有k个1的数有多少个

则ans = go(x,k) + go(10...000,k) - go((x<<1)&1,k)

判断ans与m的关系就可以不断缩小L,R的范围啦

注意:

求2^x如果用(1 << x)的话,这个时候(1 << x)是默认为int型的,

所以如果超int了,要用(1LL << x)

代码:

                                            
  //File Name: cf431D.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年07月09日 星期六 21时34分35秒
                                   

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>

#define LL long long

using namespace std;

int a[100],tot;
LL f[100][100];

void init(){
    memset(f,0,sizeof f);
    for(int i=0;i<100;i++){
        f[i][0] = 1;
        for(int j=1;j<=i;j++)
            f[i][j] = f[i-1][j] + f[i-1][j-1];
    }
}

LL go(LL x,int k){
    tot = 0;
    while(x){
        a[++tot] = x % 2;
        x >>= 1;
    }
    LL ans = 0;
    int pre = 0;
    for(int i=tot;i>0;i--){
        if(a[i] == 1)
            pre++;
        else{
            if(k - pre - 1 >= 0)
                ans += f[i-1][k-pre-1];
            else 
                break;
        }
    }
    if(pre == k)
        ans++;
    return ans;
}

LL get(LL x,int k){
    LL ans = go(x,k);
    LL y = (1LL << tot);
    //printf("x = %lld y = %lld
",x,y);
    ans = ans + go(y,k) - go(2 * x + 1,k);
    return ans;
}

LL solve(LL m,int k){
    LL l = m,r = (LL)1e18 + 1,mid;
    while(r - l > 1){
        mid = (l + r) >> 1;
        LL cur = get(mid,k);
        //printf("mid = %lld  cur = %lld
",mid,cur);
        if(cur <= m)
            l = mid;
        else
            r = mid;
    }
    if(get(l,k) == m)
        return l;
    else 
        return r;
}

int main(){
    init();
    LL m;
    int k;
    cin >> m >> k;
    cout << solve(m,k) << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/-maybe/p/5656824.html