原题: https://cn.vjudge.net/problem/Gym-101954F
题意:
给一个n位2进制数a,求有多少个n位2进制b使a+b的二进制表示有恰好k个1
解析:
因为不考虑前缀0,所以b的取值范围为0~11…11,我们就得出了a+b的范围,跑一个带上下界的dfs即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
char up[1009],down[1009];
int len,maxpos;
LL C[1009][1009];
void init(int n,int k){
//////////////C
for(int i=0;i<=1005;i++)C[i][0]=1;
for(int i=1;i<=1005;i++){
for(int j=1;j<=i;j++){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
//////////////up
len=strlen(down+1);
reverse(down+1,down+1+len);
strcpy(up+1,down+1);
up[len+1]='1';up[len+2]='\0';
//////////////maxpos
for(int i=1;;i++){
if(up[i]=='1'){
for(int j=1;j<i;j++)up[j]='1';
up[i]='0';
break;
}
}
//cout<<up+1<<endl;
maxpos=len;
if(up[len+1]=='1')maxpos++;
}
LL dfs(int pos,int res,bool fup,bool fdown){
//当前位,剩余1,上下界满足与否
if(pos==0)return res==0;
if(pos<res)return 0;
if(fup&&fdown)return C[pos][res];
//剪枝:接下来全填1 只需考量上界
if(pos==res){
if(fup)return 1;
else{
if(!(up[pos]=='1'))return 0;
else return dfs(pos-1,res-1,fup,fdown);
}
}
//剪枝:接下来全填0 只需考量下界
if(res==0){
if(fdown)return 1;
else{
if(down[pos]=='1')return 0;
else return dfs(pos-1,res,fup,fdown);
}
}
//分几种情况
LL ans=0;
if(fup&&!fdown){
if(down[pos]=='1'){
ans+=dfs(pos-1,res-1,fup,fdown);
}
else{
ans+=dfs(pos-1,res-1,fup,1);
ans+=dfs(pos-1,res,fup,fdown);
}
}
else if(!fup&&fdown){
if(!(up[pos]=='1')){
ans+=dfs(pos-1,res,fup,fdown);
}
else{
ans+=dfs(pos-1,res,1,fdown);
ans+=dfs(pos-1,res-1,fup,fdown);
}
}
else{
if((up[pos]=='1')&&(down[pos]=='1'))ans+=dfs(pos-1,res-1,fup,fdown);//1 1
else if((up[pos]=='1')&&!(down[pos]=='1'))//1 0
ans+=dfs(pos-1,res-1,fup,1),
ans+=dfs(pos-1,res,1,fdown);
else if(!(up[pos]=='1')&&!(down[pos]=='1'))//0 0
ans+=dfs(pos-1,res,fup,fdown);
}
return ans%mod;
}
int main(){
int n,k;
cin>>n>>k>>down+1;
init(n,k);
LL ans=0;
ans=dfs(maxpos,k,0,0);
printf("%lld\n",ans);
}