中石油训练赛 - Fermat‘s Optimization Problem(Java高精度运算+二分)

    科技2022-07-11  79

    题目大意:给出误差函数 ,现在给出 z 和 n,要求求出 x 和 y ,使得函数 F 的取值最小

    题目分析:首先数据范围是幂次级别的,1e5 的 10 次方,也就是 1e50 次方,需要用到大数,我用的是 java 的大数类

    因为 x 和 y 的取值是 x < y < z,考虑枚举 x 或 y ,然后二分去逼近另一个,这里我枚举的是 y ,将绝对值去掉后,得到 ,当 y 和 z 确定后,显然这个函数具有单调性,将整个式子向 0 逼近即可,即求出  的最大的可行 x1,然后尝试去更新答案,又因为绝对值是一个对称函数,不能只考虑整体小于等于 0 的最大值,还需要考虑整体大于等于 0 的最小值才行,因为 x1 是整体小于等于 0 的最大值了,所以 x1 + 1 一定是大于等于 0 的最小值,不太会证明,感性理解一下吧,所以用 x1 和 x1 + 1 各自尝试更新一下答案就好了

    需要注意的点:

    需要预处理出幂次 pow[ x ][ y ] = x^y,如果每次都调用 pow() 函数的话会 TLE只能进行一次二分,进行两次二分的话带着一个常数,会 TLE只能写二分而不能写三分,据说三分会 TLE

    代码:  

    import java.math.BigInteger; import java.util.Scanner; public class Main { static Scanner cin = new Scanner(System.in); static public BigInteger[][] pow=new BigInteger[100005][15]; static public int n,z,xx,yy; static public BigInteger ans; public static BigInteger cal(int x,int y) { return pow[x][n].add(pow[y][n]).subtract(pow[z][n]); } public static void solve(int y) { int l=1,r=y-1,mark=-1; while(l<=r) { int mid=l+r>>1; if(cal(mid,y).compareTo(BigInteger.ZERO)<=0) { mark=mid; l=mid+1; } else r=mid-1; } if(mark!=-1) { if(ans.compareTo(cal(mark,y).abs())>0) { ans=cal(mark,y).abs(); xx=mark; yy=y; } if(mark<y-1) { if(ans.compareTo(cal(mark+1,y).abs())>0) { ans=cal(mark+1,y).abs(); xx=mark+1; yy=y; } } } } public static void main(String []args) { for(int i=1;i<=100000;i++) { pow[i][0]=BigInteger.ONE; for(int j=1;j<=10;j++) pow[i][j]=pow[i][j-1].multiply(BigInteger.valueOf(i)); } BigInteger inf=BigInteger.ONE; for(int i=1;i<=6;i++) inf=inf.multiply(pow[10][10]); int kase=cin.nextInt(); while(kase-->0) { n=cin.nextInt(); z=cin.nextInt(); ans=inf; for(int y=2;y<z;y++) solve(y); System.out.printf("%d %d ",xx,yy); System.out.println(ans); } } }

     

    Processed: 0.009, SQL: 8