Description

求满足 \(n+1\sim 2n\) 之间恰有 \(m\) 个数二进制表示中有 \(k\)\(1\)\(n\),输出任意一个解即可。

Solution

容易证明 \(n+1 \sim 2n\) 中有 \(k\)\(1\) 的个数随着 \(n\) 增大而单调不降

于是二分 \(n\),问题转化为求 \(n+1 \sim 2n\) 中有 \(k\)\(1\) 的数的个数,对于每一次求 \(sum(i)\)\(1 \sim i\) 中有 \(k\)\(1\) 的数的个数

\(f[i][j][0/1]\) 表示考虑到底 \(i\) 位,\(1\) 的数量为 \(j\) 的数,前 \(i\) 位是否已经达到最大时的个数,则

如果 \(a[i]=1\)

\[f[i][j][0]=f[i-1][j][0]+f[i-1][j-1][0]+f[i-1][j][1] \\ f[i][j][1]=f[i-1][j-1][1] \]

如果 \(a[i]=0\)

\[f[i][j][0]=f[i-1][j][0] + f[i-1][j-1][0] \\ f[i][j][1]=f[i-1][j][1] \]

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 105;

int f[N][N][2],a[N],n,m,k;

int check(int n)
{
    memset(a,0,sizeof a);
    memset(f,0,sizeof f);
    for(int i=62;i>=0;--i)
    {
        a[i]=(n>>i)&1;
    }
    reverse(a,a+63);
    f[0][0][1]=1;
    for(int i=1;i<=62;i++)
    {
        for(int j=0;j<=62;j++)
        {
            if(a[i])
            {
                f[i][j][0]=f[i-1][j][0]+(j?1:0)*f[i-1][j-1][0]+f[i-1][j][1];
                if(j) f[i][j][1]=f[i-1][j-1][1];
            }
            else
            {
                f[i][j][0]=f[i-1][j][0]+(j?1:0)*f[i-1][j-1][0];
                f[i][j][1]=f[i-1][j][1];
            }
        }
    }
    return f[62][k][0]+f[62][k][1];
}

signed main()
{
    ios::sync_with_stdio(false);
    cin>>m>>k;
    int l=1,r=1e18;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(check(mid*2)-check(mid)<m) l=mid+1;
        else r=mid;
    }
    cout<<r<<endl;
}

相关文章: