>Description
作为一个神秘的电脑高手,Farmer John 用二进制数字标识他的奶牛。
然而,他有点迷信,标识奶牛用的二进制数字,必须只含有K位“1” (1 <= K <= 10)。 当然,每个标识数字的首位必须为“1”。
FJ按递增的顺序,安排标识数字,开始是最小可行的标识数字(由“1”组成的一个K位数)。
不幸的是,他没有记录下标识数字。请帮他计算,第N个标识数字 (1 <= N <= 10^7)。
>Input
第1行:空格隔开的两个整数,N和K。
>Output
如题,第N个标识数字
>Sample Input
7 3
>Sample Output
10110
>解题思路
枯了虽然有两个优化没做但是不知道为什么只拿了10分伤心子
通过手动模拟可以发现,对于一个有 i i i个1的 j j j位数,第1位数一定是1,所以其方案数为: C ( i − 1 , j − 1 ) C(i-1,j-1) C(i−1,j−1),也就是把i-1
个1放进j-1位数里面
so,我们只要一步步接近n,一个个找到每个1所在的位置输出就可以了(因为第1位为一定是1)
然后组合数不能用杨辉三角预处理(我就炸了),要用公式在线处理
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
ll n, k, t, s, ss, st;
inline ll c (int a, int b)
{
ll l = 1;
for (int i = a - b + 1; i <= a; i++) l *= i;
for (int i = 1; i <= b; i++) l /= i;
return l;
}
int main()
{
scanf ("%I64d%I64d", &n, &k);
t = k - 1, s = 0, ss = 1; //t记录当前的位数,s、ss累计方案数
while (s + ss < n)
{
s += ss;
ss = c (++t, k - 1);
}
st = t; n -= s; //st记录剩下没有输出的位数
putchar ('1');
for (k = k - 1; k; k--) //一个个找“1”
{
t = k - 1, s = 0, ss = 1;
while (s + ss < n)
{
s += ss;
ss = c (++t, k - 1);
}
n -= s;
ll p = st - t - 1;
for (; p; p--) putchar ('0');
putchar ('1');
st = t;
}
for (ll i = 1; i <= st; i++)
putchar ('0');
return 0;
}