题目定义了一种操作:将一个数变为这个数二进制下的1的个数。询问有多少个不大于n的数,进行了恰好k次操作后等于1。
数据范围(1<n<21000),观察可知,任意一个大于1000的数进行一次操作后都会变成小于等于1000的数,因此可以枚举1000以内的数变为1的操作个数,当某个数x的操作数为m-1时,统计长度小于等于n的位数下所有满足1的个数为x的数的数量,易知1的位置摆放为组合,所以需要预处理组合数
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int mod=1000000007; char s[1314]; ll C[1010][1010]; ll l; int a[1314]; int num[1314]; ll total=0; int wei(ll x) { int cnt=0; while(x) { cnt+=(x&1); x>>=1; } return cnt; } void dfs(int dep,int now,int flag)//d几位now几个1 { if(now>l)return; if(now==0){ total=(total+1)%mod; return; } if(dep==0)return; if(flag==0)//放0则后面若干个1排列组合 { total=(total+C[dep][now])%mod; } else { if(a[dep]==0) { dfs(dep-1,now,flag); } if(a[dep]==1) { dfs(dep-1,now-1,flag); dfs(dep-1,now,0); } } } int main(){ ll m; cin>>s+1; l=strlen(s+1); cin>>m; if(m==0) { cout<<1; return 0; } if(m==1) { cout<<l-1; return 0; } for(int i=0;i<=l;i++) { C[i][0]=1; } for(int i=1;i<=l;i++) { for(int j=1;j<=i;j++) { C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; // cout<<C[i][j]<<" "; } //cout<<endl; } for(int i=1;i<=l;i++) { a[l-i+1]=s[i]-‘0‘; } /*for(int i=l;i>=1;i--) { cout<<a[i]; } cout<<endl;*/ num[1]=0; for(int i=2;i<=1000;i++) { int w=wei(i); //cout<<w<<endl; num[i]=num[w]+1; if(num[i]==m-1) { dfs(l,i,1); } } cout<<total; return 0; }