原题链接:http://poj.org/problem?id=3685
这道题题意就是给定一个n*n的矩阵A,A(i,j)= i2 + 100000 × i + j2 - 100000 × j + i × j,求其中第M小元素
思路:这道题是《挑战程序设计竞赛》后面的练习题之一,书上是将该题划分到"查找第K大的值"这一部分
那么之后应该如何解决呢?二分答案!
假设二分到某数值是value,那么可以统计A(i,j)<=value的数目k。如果k<M,说明这个value值还不够大,
否则,答案不超过value。如此二分下去便能确定出答案。
那么如何统计上面的k呢?显然,该不等式是一个二次不等式。如果再循环i的话,那么就变成一元二次不等式了。
这样处理,便可以求出第i行内满足不等式的j的数目。这些数目求和即是k
我的代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
class Reader{
static BufferedReader reader;
static StringTokenizer tokenizer;
static void init(InputStream input) {
reader=new BufferedReader(new InputStreamReader(input));
tokenizer=new StringTokenizer("");
}
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer=new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException{
return Integer.parseInt(next());
}
static long nextLong() throws IOException {
return Long.parseLong(next());
}
}
public class Main {
static int casenum;
static long n,m,maxnum,minnum;
static long cal(long b,long c) {
double db=b;
double dc=c;
long detnum=b*b-4*c;
double det=(double)detnum;
if (det<0) return 0;
else {
double x1=(db-Math.sqrt(det))/2;
double x2=(db+Math.sqrt(det))/2;
long xnum2=(long)x2;
double x1l=(long)x1;
if (Math.abs(x1-x1l)>=1e-8) x1l=x1l+1;
long xnum1=(long)x1l;
if (xnum2>n) xnum2=n;
if (xnum1<1) xnum1=1;
if (xnum2<xnum1) return 0;
else return (xnum2-xnum1+1);
}
}
private static long count(long valuenum) {
long sum=0;
for (int i=1;i<=n;i++) {
long inum=i;
long b=100000-inum;
long c=inum*inum+100000*inum-valuenum;
sum+=cal(b, c);
}
return sum;
}
private static void deal() {
long left=minnum;
long right=maxnum;
long mid;
long ans=left;
while (right>=left) {
mid=(left+right)/2;
long k=count(mid);
if (k<m) left=mid+1;
else {
ans=mid;
right=mid-1;
}
}
System.out.println(ans);
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Reader.init(System.in);
casenum=Reader.nextInt();
for (int t=1;t<=casenum;t++) {
n=Reader.nextLong();
m=Reader.nextLong();
maxnum=n*n*2+100000*n+n*n;
minnum=1*1*2-100000*n+1*1;
deal();
}
}
}
注意能用long就用long,我本来样例过了结果WA,后来看了讨论版块,修改了下,过了里面的数据才AC。
(我修改的是count方法里面的代码,这里如果直接用循环的int类型的i乘的话就会出错)