[CF431D] Random Task – 数位dp,二分

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;
}

原文链接: https://www.cnblogs.com/mollnn/p/13323006.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    [CF431D] Random Task - 数位dp,二分

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/367150

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 下午5:30
下一篇 2023年3月2日 下午5:31

相关推荐