参考资料:算法合集之《浅谈数位类统计问题》
import java.math.*;
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int k = input.nextInt();
int m = input.nextInt();
BigInteger[][] f = new BigInteger[105][105];
BigInteger[] g = new BigInteger[105];
cal_f(f, k);
cal_g(f, g, k);
BigInteger one = BigInteger.valueOf(1);
BigInteger two = BigInteger.valueOf(2);
BigInteger max = (two.pow(k)).subtract(one);
System.out.println(divide(f, g, max, k, m));
}//main
//f[i][j] 高度为i的完全二叉树,可以构成的有j个1的二进制数的个数
public static void cal_f(BigInteger[][] f, int height){
BigInteger zero = BigInteger.valueOf(0);
BigInteger one = BigInteger.valueOf(1);
for (int i = 1; i <= height; i++){ //initialize
for (int j = 0; j <= height; j++){
f[i][j] = zero;
}
}
f[1][1] = one;
f[1][0] = one;
for (int i = 2; i <= height; i++){
f[i][0] = one;
for (int j = 1; j <= i; j++){
f[i][j] = f[i-1][j].add(f[i-1][j-1]);
}
}
} //cal_f
//g[i] 高度为i的完全二叉树的形成的二进制数中各个数位上1的总数
public static void cal_g(BigInteger[][] f, BigInteger[] g, int height){
BigInteger zero = BigInteger.valueOf(0);
for (int i = 0; i <= height; i++){
g[i] = zero;
for (int j = 1; j <= i; j++) { //1的数量
g[i] = g[i].add(f[i][j].multiply(BigInteger.valueOf(j)));
}
}
} //cal_g
public static BigInteger divide(BigInteger[][] f, BigInteger[] g, BigInteger max, int k, int m){
BigInteger bigK = BigInteger.valueOf(k);
BigInteger zero = BigInteger.valueOf(0);
BigInteger one = BigInteger.valueOf(1);
BigInteger two = BigInteger.valueOf(2);
BigInteger left = one;
BigInteger right = bigK.multiply(max);
while (left.compareTo(right) < 0) {
BigInteger mid = (left.add(right)).divide(two);
BigInteger tot = zero;
BigInteger cur = zero;
int groups = 0;
while (groups < m){
BigInteger[] tmp = find(g, tot.add(mid), k);
cur = tmp[0];
// System.out.println("left = " + left + " right = " + right + " mid = " + mid + " cur = " + cur + " tot = " + tmp[1]);
if (cur.compareTo(max) <= 0) {
++groups;
tot = tmp[1];
}
else{ //超出右边的范围
break;
}
} //groups < m
if (cur.compareTo(max) < 0) { //没到右边就分完组了
++groups;
}
if (groups > m){
left = mid.add(one);
}
else{
right = mid;
}
}//left < r
return(left);
}//divide
public static BigInteger[] find(BigInteger[] g, BigInteger max, int k){
BigInteger zero = BigInteger.valueOf(0);
BigInteger one = BigInteger.valueOf(1);
BigInteger two = BigInteger.valueOf(2);
BigInteger tot = zero;
BigInteger ans = zero;
BigInteger num = zero; //前面有几个1
for (int i = k; i >= 0; i--){
if (tot.add(g[i]).add(one).add(num.multiply(two.pow(i))).compareTo(max) <= 0){
ans = ans.add(two.pow(i));
tot = tot.add(g[i]).add(one).add(num.multiply(two.pow(i)));
num = num.add(one);
}
}
BigInteger[] tmp = {ans, tot};
return(tmp);
} //find
}