题目链接:http://poj.org/problem?id=3685
Matrix
Time Limit: 6000MS | Memory Limit: 65536K | |
Total Submissions: 7378 | Accepted: 2187 |
Description
Given a N × N matrix A, whose element in the i-th row and j-th column Aij is an number that equals i2 + 100000 × i + j2 - 100000 × j + i × j, you are to find the M-th smallest element in the matrix.
Input
The first line of input is the number of test case.
For each test case there is only one line contains two integers, N(1 ≤ N ≤ 50,000) and M(1 ≤ M ≤ N × N). There is a blank line before each test case.
Output
For each test case output the answer on a single line.
Sample Input
12 1 1 2 1 2 2 2 3 2 4 3 1 3 2 3 8 3 9 5 1 5 25 5 10
Sample Output
3 -99993 3 12 100007 -199987 -99993 100019 200013 -399969 400031 -99939
Source
POJ Founder Monthly Contest – 2008.08.31, windy7926778
题解:
1.二分这个数mid,然后计算有多少对(i,j),使得F= i^2 + 100000 × i + j^2 - 100000 × j + i × j <= mid。如果符合,则缩小mid,否则增大mid。
2.问:怎么计算有多少对(i,j)使得 F <= mid 呢?
答:根据观察,式子“F = i^2 + 100000 × i + j^2 - 100000 × j + i × j”为二元二次方程,当i确定时,F就成了关于j的一元二次方程。所以枚举i,然后计算 有多少个整数j,使得 F = j^2 + (i-1e5)*j + i^2 + i*1e5 - mid <= 0。根据高中的知识,我们需要求解出函数F的两个零点x1和x2(1<=x1<=x2<=n),然后再从区间[x1,x2]取出整数,即得到了满足约束的多对(i,j)。
正确代码:(求最小的数,使得小于等于它的数的个数>=m。即为题目所求)

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 #define ms(a,b) memset((a),(b),sizeof((a))) 13 using namespace std; 14 typedef long long LL; 15 const double EPS = 1e-8; 16 const int INF = 2e9; 17 const LL LNF = 2e18; 18 const int MAXN = 1e3+10; 19 20 LL n, m; 21 22 bool test(LL tmp) 23 { 24 LL sum = 0; 25 for(LL i = 1; i<=n; i++) //枚举i。当i已确定时, 剩下的式子就是关于j的一元二次方程。求解两个根。 26 { 27 LL a = 1, b = i-100000, c = 1LL*i*i+1LL*i*100000-tmp; 28 if(1LL*b*b-4LL*a*c<0) continue; //无实数根时, 下一个i 29 LL x1 = max( 1LL, (LL)ceil((-b-sqrt(1LL*b*b-4LL*a*c))/(2*a)) ); //左根向上取整,最小只能为1。 30 LL x2 = min( 1LL*n, (LL)floor((-b+sqrt(1LL*b*b-4LL*a*c))/(2*a)) ); //右根向下取整,最大只能为n 31 sum += max( 0LL, x2-x1+1 ); //区间内有多少个整数 32 } 33 return sum>=m; 34 } 35 36 int main() 37 { 38 int T; 39 scanf("%d", &T); 40 while(T--) 41 { 42 scanf("%lld%lld", &n, &m); 43 LL l = -2e10, r = 2e10; 44 while(l<=r) //二分答案 45 { 46 LL mid = (l+r)>>1; 47 if(test(mid)) 48 r = mid - 1; 49 else 50 l = mid + 1; 51 } 52 printf("%lld ", l); 53 } 54 }
错误代码:(求最大的数,使得小于它的数的个数<m。为题目所求的上一个数)

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 #define ms(a,b) memset((a),(b),sizeof((a))) 13 using namespace std; 14 typedef long long LL; 15 const double EPS = 1e-8; 16 const int INF = 2e9; 17 const LL LNF = 2e18; 18 const int MAXN = 1e3+10; 19 20 LL n, m; 21 22 bool test(LL tmp) 23 { 24 LL sum = 0; 25 for(LL i = 1; i<=n; i++) //枚举i。当i已确定时, 剩下的式子就是关于j的一元二次方程。求解两个根。 26 { 27 LL a = 1, b = i-100000, c = 1LL*i*i+1LL*i*100000-tmp; 28 if(1LL*b*b-4LL*a*c<=0) continue; 29 LL x1 = max( 1LL, (LL)ceil((-b-sqrt(1LL*b*b-4LL*a*c))/(2*a)) ); //左根向上取整,最小只能为1。 30 LL x2 = min( 1LL*n, (LL)floor((-b+sqrt(1LL*b*b-4LL*a*c))/(2*a)) ); //右根向下取整,最大只能为n 31 sum += max( 0LL, x2-x1+1 ); //区间内有多少个整数 32 } 33 return sum<m; 34 } 35 36 int main() 37 { 38 int T; 39 scanf("%d", &T); 40 while(T--) 41 { 42 scanf("%lld%lld", &n, &m); 43 LL l = -2e10, r = 2e10; 44 while(l<=r) //二分答案 45 { 46 LL mid = (l+r)>>1; 47 if(test(mid)) 48 l = mid + 1; 49 else 50 r = mid - 1; 51 } 52 printf("%lld ", r); 53 } 54 }