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;
}
标签:cnt,full,int,ll,pos,CF,2n,DP,431
From: https://www.cnblogs.com/fengxue-K/p/17984514