Codeforces Contest 431 D记忆化搜索(数位DP)+二分

202 篇文章 6 订阅
82 篇文章 1 订阅

This way

题意:

让你求出一个n,使得从n+1到2n之间有正好m个二进制位上为1的个数=k的数
3->11 两个1

题解:

这道题满足二分的性质,来证明一下:
假设当前的区间为n~2n,当n->n+1时,2n->2n+2,我们少了一个n+1,但是多了2n+1,和2n+2,2n+2与n+1的位数是相同的,相当于多了一个2n+1,那么n越大,满足条件的可能越多,这个曲线满足二分的性质,所以在二分的时候做数位DP即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[70][70];
int a[70];
ll m,k;
ll dfs(int pos,int num,int mx)
{
    if(pos<0)
        return num==k;
    if(~dp[pos][num]&&!mx)
        return dp[pos][num];
    ll ans=0;
    int top=mx?a[pos]:1;
    if(num==k)
        top=0;
    for(int i=0;i<=top;i++)
        ans+=dfs(pos-1,num+(i==1),mx&&i==top);
    if(!mx)
        dp[pos][num]=ans;
    return ans;
}
int init(ll x)
{
    ll ret=1;
    int ans,cnt=0;
    while(ret<=x)
        ret*=2,cnt++;
    ret/=2,cnt--;
    ans=cnt;
    while(ret)
    {
        a[cnt--]=(x&ret)?1:0;
        ret/=2;
    }
    return ans;
}
int main()
{
    scanf("%lld%lld",&m,&k);
    ll low=1,high=1e18,mid;
    while(high>=low)
    {
        mid=low+high>>1;
        memset(dp,-1,sizeof(dp));
        ll r=dfs(init(mid*2),0,1);
        memset(dp,-1,sizeof(dp));
        ll l=dfs(init(mid),0,1);
        if(r-l>m)
            high=mid-1;
        else if(r-l<m)
            low=mid+1;
        else
            break;
    }
    printf("%lld\n",mid);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值