题目描述
作为一个神秘的电脑高手,Farmer John 用二进制数字标识他的奶牛。
然而,他有点迷信,标识奶牛用的二进制数字,必须只含有K位“1” (1 <= K <= 10)。 当然,每个标识数字的首位必须为“1”。
FJ按递增的顺序,安排标识数字,开始是最小可行的标识数字(由“1”组成的一个K位数)。
不幸的是,他没有记录下标识数字。请帮他计算,第N个标识数字 (1 <= N <= 10^7)。
输入
第1行:空格隔开的两个整数,N和K。
输出
如题,第N个标识数字
输入样例
7 3
输出样例
10110
解
组合,暴力,推演?
不停寻找个lln,使 lln位填k位的方案数 < n <= lln+1位填k位的方案数(就是说第n大的数在lln位数中最大的数和lln+1位数中最大的数中间),然后在lln+1位填上1,用n减去lln作新的n,然后寻求的k减去1.
Code
#include<cstdio>
#define LLL long long
LLL n,k;
LLL Ans[10000001];
LLL C(LLL nn, LLL mm){ //组合:n里取m个的方案数
LLL lll = 1;
for(LLL i = nn-mm+1; i <= nn; ++i) lll = lll * i;
for(LLL i = 2; i <= mm; ++i) lll = lll/i;
return lll;
}
LLL cl(LLL ln, LLL kk){ //求第ln个数,kk=剩余几个1没填
if(ln == 0 || kk == 0) return 0;
LLL ans = 0,lln = kk;
while(ans<n){
ans = C(lln,kk); //lln位填满所能得到的方案数
if(ans >= ln){ //如果这么填超过了要求的(其实就是找到了要填1的地方
Ans[lln] = 1; //填上个1
if(lln>kk) ans=cl(ln-C(lln-1,kk),kk-1); //下一位之后,填kk-1个1
break;
}
++lln; //少于,加多一位试试
}
return lln;
}
int main(){
scanf("%lld%lld",&n,&k);
for(LLL i = cl(n,k); i; --i)
putchar(48+Ans[i]); //快输
}