CF-431-D-二分+数位DP

431-D 题目大意

请你找到一个数n,满足区间[n+1,2n]中恰有m个数的二进制表示中有k1


Solution

这种区间中计数类型的题目首先相当数位DP。

但是这里缺乏上下界,难点就在于观察到n的单调性([n+1,2n]中有k1的数是单调不减的),简要证明:

  • 对于一个n对应的数为n+1,n+2,····,2n1,2n
  • 而对于n+1对应的数为n+2,n+3,····,2n1,2n,2n+1,2n+2

这里面前者多了个n+1,后者多了2n+1,2n+2两个数,其余数都一样,这里n+12n+2的二进制表示中1的个数相同,因为他们是两倍的关系,那么相当于后者多出来一个2n+1,因此具有单调性。

二分答案n,数位DP计数,时间复杂度O(log2U)

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

ll f[66][66];

ll calc(ll mid,int k){
    ll l=mid,r=mid*2;
    vector<int> num(64);
    function<ll(int,int,int)> dp=[&](int pos,int full,int cnt)->ll{
        if(pos<0) return cnt==k;
        if(pos>=num.size()) return dp(pos-1,full,cnt);
        if(!full&&f[pos][cnt]!=-1) return f[pos][cnt];
        ll res=0;
        for(int i=0;i<=1;i++){
            if(full&&i>num[pos]) break;
            int nxtfull=(full&&i==num[pos]);
            int nxtcnt=cnt+i;
            if(nxtcnt<=k) res+=dp(pos-1,nxtfull,nxtcnt);
        }
        if(!full) f[pos][cnt]=res;
        return res;
    };
    for(int i=63;~i;i--){
        if(l&(1LL<<i)) num[i]=1;
        else num[i]=0;
    }
    ll low=dp(63,1,0);
    for(int i=63;~i;i--){
        if(r&(1LL<<i)) num[i]=1;
        else num[i]=0;
    }
    ll high=dp(63,1,0);
    return high-low;
}

void solve(){
    ll m,k;
    cin>>m>>k;
    ll l=1,r=1e18;
    memset(f,-1,sizeof f);
    while(l<r){
        ll mid=(l+r)>>1;
        if(calc(mid,k)<m) l=mid+1;
        else r=mid;
    }
    cout<<l<<'\n';
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

posted @   fengxue-K  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示