POJ-3685-Matrix

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

分析:一开始就推出了一般规律,想着打表矩阵然后查表ac的,没想到再n>25000时,矩阵的顺序就会变了,导致后面疯狂的wa,后来看了题解才知道,是个二分套二分的题目,其实我们可以发现当 j 不变的情况下,随着 i 的增大,Aij也相应增大,由这个受到启发,二分枚举第M小的数,然后按列寻找,找到第一个大于这个数的位置,就可以知道该列中有多少个数是大于这个数的了

#include<stdio.h>
#include<iostream>
#include<map>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<string>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
#define MAXN 100005*5
#define INF 0x3f3f3f3f//将近int类型最大数的一半,而且乘2不会爆int
#define MOD 1000000007

ll ccc(ll i, ll j)
{
    return i * i + 100000 * i + j * j - 100000 * j + i * j;
}

int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        ll n, m;
        scanf("%lld%lld", &n, &m);
        ll l = -10000000000000LL, r = 1000000000000000LL;
        ll mid, ans;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            ll num = 0;
            for (int i = 1; i <= n; ++i)  	//枚举每一列
            {
                ll L = 1, R = n, midd, tem = 0;
                while (L <= R)
                {
                    midd = (L + R) >> 1;
                    if (ccc(midd, i) <= mid)
                    {
                        tem = midd;
                        L = midd + 1;
                    }
                    else
                        R = midd - 1;
                }
                num += tem;//求出该列小于等于mid的个数
            }
            if (num < m)
                l = mid + 1;
            else  		 //注意最小的那个数就是恰好的第k小的那个,因为再比他小的话,肯定没有k个
            {
                ans = mid;
                r = mid - 1;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

最后附上我规律打表疯狂wa的代码纪念一下:

#include<stdio.h>
#include<iostream>
#include<map>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<string>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
#define MAXN 100005*5
#define INF 0x3f3f3f3f//将近int类型最大数的一半,而且乘2不会爆int
#define MOD 1000000007

ll a[MAXN], b[MAXN];
void init() {
	ll ret = 1;
	a[0] = 0;
	for (ll i = 1; i <= 50005; ++i) {
		a[i] = a[i - 1] + ret;
		ret++;
	}
}

int main()
{
	init();
	ll t;
	cin >> t;
	while (t--) {
		memset(b, 0, sizeof(b));
		ll n, m;
		scanf("%lld %lld", &n, &m);
		b[1] = n * (n + 1) / 2;
		ll tem = n - 1;
		for (ll i = 2; i <= n; ++i) {
			b[i] = b[i - 1] + tem;
			tem--;
		}
		ll x, y, cnt, flag=1;
		for (ll i = 1; i <= n; ++i) {
			if (m <= a[i]) {
				flag = 0;
				x = m - a[i - 1];
				y = n-i+x;
				break;
			}
		}
		if (flag) {
			for (ll i = 2; i <= n; ++i) {
				if (m <= b[i]) {
					y = m - b[i - 1];
					x = i + y - 1;
					break;
				}
			}
		}
		ll sum = x * x + 100000 * x + y * y - 100000 * y + x * y;
		cout << sum << '\n';
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值