2018-2019焦作区域赛E大数 & JAVA BigInteger、BigDecimal 整理

大致题意:当 i 是平方数(平方根大于二)的整数倍时, = \infty ,相当于断路,否则 =i 。

S_i 是电阻的集合,当 j 是 i 的因数时 r_j \in S_i (包括 r_1r_i)。

给出 n 代表前 n 个集合,选出 n 个集合中的电阻并联结果最小的那个集合,输出并联电阻。

容易发现最后的结果是关于n的阶梯型函数f(n).

n=1 时 f(1)=1;

\in [2,6) 时 f(n) = 2/3;

\in [6,30) 时 f(n) = 1/2;

\in [30,210) 时 f(n) = 5/12;

······

规律就是,前 i 个素数的前缀积MUL为f(n)的第 i 个跳跃间断点。

并联越多小电阻,总电阻越小,那就从最小的电阻开始选,越多越好。因为平方数下标的电阻断路,所以每个质因子只用考虑一次。能选第 i 个素数时,n 应该不小于前 i 个素数的乘积,这样才能保证前 i 个素数在前 i 个集合中同时出现在某一个集合内。

选出包含前 i 个素数的集合之后,发现该集合内的电阻包括 该 i 个素数 和 该 i 个素数所有组合出的乘积。硬求肯定爆炸了。

设已经求出的电阻倒数之和为sum,下一个素数为p。则加入下一个电阻时有  sum = sum*(1+1/p),这样就可以很顺利的求出最终结果的倒数。因为要输出成分数形式,所以在递推的过程要分别保存分子和分母,最后输出前记得约分。

前 i 个素数的前缀积在 i 不到60时就超过了10^{100},而 t 不超过100,可以打表前60个素数之后每次重新递推。

import java.util.*;
import java.math.*;
public class Main {

	    public static void main(String[] args) {
	        Scanner input = new Scanner(System.in);
	        BigInteger num_1 = new BigInteger("1");
	        BigInteger num_2 = new BigInteger("2");
	        BigInteger num_3 = new BigInteger("3");
	        BigInteger num_0 = new BigInteger("0");
	        BigInteger fz,fm;
	        BigInteger sum;
	        BigInteger now;
	        int[] prime = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269};
	        int t,i;
	        String S;
	        t = input.nextInt();
	        S = input.nextLine();
	        while(t-- > 0) {
	            i = 0;
	            fz = new BigInteger("1");
	            fm = new BigInteger("1");
	            sum = new BigInteger("1");
	            S = input.nextLine();
	            now = new BigInteger(S);
	            while(now.compareTo(sum.multiply(new BigInteger(String.valueOf(prime[i])))) >= 0) {
	                BigInteger temp = new BigInteger(String.valueOf(prime[i++]));
	                sum = sum.multiply(temp);
	                fz = fz.multiply(temp.add(num_1));
	                fm = fm.multiply(temp);
	                temp = fz.gcd(fm);

	                if(temp.compareTo(num_1)!=0) {
	                	fz = fz.divide(temp);
	                	fm = fm.divide(temp);

	                }
	            }
	            System.out.println("" + fm + "/" + fz);
	        }
	    }

	}

C++没存大数模板,就算有也感觉很难敲,不如直接用JAVA的大数,队伍打算以后遇到大数就用JAVA写。但是JAVA大数又用得很少,不熟悉。那就顺便总结一下吧。

部分转载于:https://blog.csdn.net/reigns_/article/details/81672069

对大整数来说,用BigInteger:

1.读入:

//从开始读到文件尾
    Scanner cin = new Scanner(System.in); //相当于C++中的cin,只不过这个cin需要自己创建
    while(cin.hasNext()){  //等同于!=EOF
        BigInteger a;
        a = cin.nextBigInteger();   //读入一个BigInteger;
        System.out.println(a);   //输出a并换行
    }
//T组样例:
    Scanner cin = new Scanner(System.in);
    int T = cin.nextInt();
    while (T-- > 0) {
        BigInteger a,b;
        a = cin.nextBigInteger();   //读入一个BigInteger;
        b = cin.nextBigInteger();   //读入一个BigInteger;
    }

2.构造:

BigInteger a=new BigInteger(“123”);  //第一种,参数是字符串
String S;
···
BigInteger a=new BigInteger(S);    //第一种,参数是字符串
BigInteger a=BigInteger.valueOf(123);    //第二种,参数可以是int、long

3.常数:(之前不会,还自己构造。。)

BigInteger.ZERO    //大整数0
BigInteger.ONE    //大整数1
BigInteger.TEN   //大整数10

4.常用方法:

//加
 a = a.add(b);    // a += b
//减
 a = a.subtract(b);   // a -= b
//乘
 a = a.divide(b);  // a /= b
//除
 a = a.multiply(b);    // a *= b
//取模
 a = a.mod(b);    //a %= b;
 a = a.remainder(b);    //a %= b;
//指数
 a = a.pow(b);    // a = a^b(a的b次方,不是取异或)
//gcd
 a = a.gcd(b);    // a = gcd(a,b);
//abs
 a = a.abs();    //取绝对值
//比较
 a.compareTo(b)    //a小于b返回-1,等于返回0,大于返回1
 a.equals(b)    //返回真假逻辑值
--------------//例子: n.compareTo(BigInteger.ZERO)==0  //相当于n==0
--------------//例子: n.equals(BigInteger.ZERO)  //结果是逻辑值
//取最大、最小
 a = a.max(b);  //求最大值 a = max(a,b)
 a = a.min(b);  //求最小值 a = min(a,b)
//求大整数位数
 len = a.toString().length();   //a的类型为BigInteger


 

对大精度小数来说,用BigDecimal:

与BigInteger大致相同,在读入和构造的时候主要通过字符串类转化过去,或者用valueOf方法。

加减乘的方法签名相同。至于除法就有些不一样了:

https://www.jianshu.com/p/881afd68316e

https://www.jianshu.com/p/2947868d76eb

public BigDecimal divide (BigDecimal value,int scale,RoundingMode mode);

 divide方法可以只有第一个参数,但是只有第一个参数时,如果结果是无限小数则会抛出异常,所以要设置第二个参数(

表示小数位数)和第三个参数(表示取舍方式,一般用  BigDecimal.ROUND_HALF_UP 或者 BigDecimal.ROUND_CEILING即四舍五入)

不能只设置前两个参数。

还有一个直接设置精度的方法:

public BigDecimal setScale(int scale, RoundingMode roundingMode)

 两个参数都不能省。

 


 

 

 

你可能感兴趣的:(JAVA)