SPOJ2319 BIGSEQ - Sequence 数位DP+java大数

参考资料:算法合集之《浅谈数位类统计问题》
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
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值