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

431-D 题目大意

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


Solution

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

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

  • 对于一个\(n\)对应的数为\(n+1,n+2,····,2n-1,2n\)
  • 而对于\(n+1\)对应的数为\(n+2,n+3,····,2n-1,2n,2n+1,2n+2\)

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

二分答案\(n\),数位DP计数,时间复杂度\(O(log^2U)\)

#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 @ 2024-01-24 13:42  fengxue-K  阅读(34)  评论(0)    收藏  举报