题目大意:给出误差函数 ,现在给出 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);
}
}
}