UPC Participate in E-sports(参加电子竞技)(Biginteger的平方根:二分或牛顿迭代法)

Jessie and Justin want to participate in e-sports. E-sports contain many games, but they don't know which one to choose, so they use a way to make decisions.

Je和Ju想去参加电子竞技,电竞有很多项目,但是他们不知道选哪一个,所以他们用一种方式去决策。
           They have several boxes of candies, and there are i candies in the i^th box, each candy is wrapped in a piece of candy paper. Jessie opens the candy boxes in turn from the first box. Every time a box is opened, Jessie will take out all the candies inside, finish it, and hand all the candy papers to Justin.

他们有几个糖果盒,在第i个糖果盒内有i个糖,每一个糖都由糖果纸包裹着,Je从第一个糖果盒开始依次打开糖果盒,每一回糖果盒被打开,Je都会拿出里面的所有糖,吃光它,并把糖纸都给Ju.

When Jessie takes out the candies in the N^th box and hasn't eaten yet, if the amount of candies in Jessie's hand and the amount of candy papers in Justin's hand are both perfect square numbers, they will choose Arena of Valor. If only the amount of candies in Jessie's hand is a perfect square number, they will choose Hearth Stone. If only the amount of candy papers in Justin's hand is a perfect square number, they will choose Clash Royale. Otherwise they will choose League of Legends.Now tell you the value of N, please judge which game they will choose.

当Je拿出第N个盒子里的糖果时,他还没来得及吃。如果Je手里的糖果数和Ju手里的糖果纸数都是一个完全平方数,他们会选王者荣耀,如果只有je手里的糖果数才是一个平方数,那么他们选炉石传说,如果只有Ju手里的糖果纸数才是一个平方数,他们会选皇室战争,否则就选英雄联盟。现在告诉你N的数值,请判断他们会选哪款游戏。

输入

The first line contains an integer T(1≤T≤800), which is the number of test cases. 
Each test case contains one line with a single integer: N(1≤N≤10^200).

输出

For each test case, output one line containing the answer.

根据题意,Je还没有吃第N个盒子里的糖果,其他被吞了,且糖果纸都给了Ju,所以他手里的糖果数是N个,而Ju手中的糖果纸数是1...n-1的自然数序列和。那么主要任务是判断n和(n-1)*n/2是不是完全平方数。数据范围是1~10^200,很明显不是一般的C++数据类型就可以承受得了的,所以考虑使用Java封装的Biginteger和Bigdecimal类型。

然而Java没有封装的BigInteger的平方根函数,所以必须自己想出一个办法解决这个问题。我们都知道二分可以求函数或者方程的根,那么相应的,a平方根就是(x^2-a=0)这个方程的解,所以只要用二分法解这个方程就可以求得平方根。

import java.util.*;
import java.math.*;
public class Main
{
    public static boolean binary(BigInteger left,BigInteger right,BigInteger n)//二分函数
    {
        BigInteger cons2=BigInteger.valueOf(1);
        BigInteger cons=BigInteger.valueOf(2);
        while(left.compareTo(right)<=0)
        {
            BigInteger mid=(left.add(right)).divide(cons);
            BigInteger tmp=mid.multiply(mid);
            if(tmp.compareTo(n)==0)
            {
                return true;
            }
            else if(tmp.compareTo(n)>0)
            {
                right=mid.subtract(cons2);
            }
            else
            {
                left=mid.add(cons2);
            }
 
        }
        return false;
    }
    public static void main(String args[])
    {
        Scanner cin=new Scanner(System.in);
        int t;
        t=cin.nextInt();
        BigInteger cons6=BigInteger.valueOf(1);
        BigInteger cons5=BigInteger.valueOf(2);
        for(int i=1;i<=t;i++)
        {
            BigInteger n=cin.nextBigInteger();
            BigInteger left=BigInteger.valueOf(1),right=n;
            Boolean sign=false,sign2=false;
            if(binary(left, right, n)==true)
                sign=true;
            BigInteger tmp33=(n.multiply(n.subtract(cons6))).divide(cons5);
            if(n.compareTo(cons6)==0||n.compareTo(cons5)==0)
                sign2=true;
            else
                if(binary(left,tmp33,tmp33)==true)
                    sign2=true; 
            //System.out.println(sign+" "+sign2);
            if(sign&&sign2)
            {
                System.out.println("Arena of Valor");
            }
            else if(sign==false&&sign2==true)
            {  
                System.out.println("Clash Royale");
            }
            else if(sign==true&&sign2==false)
            {
                System.out.println("Hearth Stone");
            }
            else
                System.out.println("League of Legends");
        }
        cin.close();
    }
}

当然,还有一种不推荐的办法是牛顿迭代法,在这个题里测试的结果是会MLE+TLE,不过作为一种好用的办法可以多多学习一下。

何谓牛顿迭代法?简而言之就是求函数某点切线,再求得本切线与x轴交的横坐标,再代入原式,从这个点继续作切线,如此往复,最终可以逼近答案。(仅仅讨论平方根,实际上这个可以求N次方根)

上面有说,解一个实数a的平方根的过程就是在解x^{2}-a=0,同样也就是在找 f(x)=x^2-a这个函数的零点。

 

  1. 那么假定函数上一个点为(x0,f(x0)),从这个点作切线,根据点斜式可知其方程:y-f(x0)=f'(x0)(x-x0)
  2. 下一步解出它在x轴上的交点,即当y=0时,x=\frac{x0*f'(x0)-f(x0)}{f'(x0)}
  3. 代入f(x)的表达式,x=\frac{1}{2}*x0+\frac{a}{2x0},这就是主要的表达式依据。因为接下来的所有过程都是重复这个。
import java.util.*;
import java.math.*;
import java.text.*;
public class Main
{
    public static BigInteger SquareRoot(BigInteger parameter)
    {
        函数表达式f(x)=x^2-a
        BigDecimal a=new BigDecimal(parameter);这是常数a
        BigDecimal x=BigDecimal.ONE;这是x0,假如它是1
        while((x.multiply(x).subtract(a)).abs().compareTo(BigDecimal.valueOf(0.0001))>0)
        //当|x^2-a|>0.0001,也就是求得的平方根的平方与原数的差距不能大于这个数,收敛半径为1e-4
        {
            BigDecimal tmp1=a.divide(x,2000,BigDecimal.ROUND_HALF_UP);
            //原文主要表达式的x/a部分
            BigDecimal tmp2=tmp1.add(x);
            //主要表达式的x+x/a部分
            x=tmp2.divide(BigDecimal.valueOf(2),2000,BigDecimal.ROUND_HALF_UP);
            //主要表达式:(1/2)*x+x/(2a)
        }
        BigInteger ans=x.toBigInteger();
        return ans;
    }
    public static void main(String args[])
    {
        Scanner cin=new Scanner(System.in);
        int t=cin.nextInt();
        for(int i=1;i<=t;i++)
        {
            BigInteger n=cin.nextBigInteger();
            BigInteger SumN=n.multiply(n.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2));
            BigInteger root1=SquareRoot(n),root2=SquareRoot(SumN);
            boolean sign1=false,sign2=false;
            if(root1.multiply(root1).compareTo(n)==0)
                sign1=true;
            if(root2.multiply(root2).compareTo(SumN)==0)
                sign2=true;
            if(sign1&&sign2)
                System.out.println("Arena of Valor");
            else if(sign1&&!sign2)
                System.out.println("Hearth Stone");
            else if(!sign1&&sign2)
                System.out.println("Clash Royale");
            else
                System.out.println("League of Legends");
        }
        cin.close();
    }
}

 

posted @ 2019-08-21 11:22  完全墨染的樱花  阅读(137)  评论(0编辑  收藏  举报