zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    如何关闭微软反恶意软件客户端
    年轻爸爸家长会上猝死,事前已连续发烧三天
    人猝死前身体发出两个救命信号,一定要清楚,关键时刻救命
    火遍世界的视力恢复法 现在知道还不晚!
    重视工龄工资,食堂一定自己办
    VR行业纷纷倒闭:有硬件没内容
    企业为什么要去美国建厂
    bzoj 1046 : [HAOI2007]上升序列 dp
    bzoj 1857: [Scoi2010]传送带 三分
    bzoj 1045: [HAOI2008] 糖果传递
  • 原文地址:https://www.cnblogs.com/-maybe/p/5656824.html
Copyright © 2011-2022 走看看