Poj 3685(经典二分)

Matrix
Time Limit: 6000MS Memory Limit: 65536K
Total Submissions: 4150 Accepted: 1007

Description

Given a N × N matrix A, whose element in the i-th row andj-th column Aij is an number that equals i2 + 100000 ×i + j2 - 100000 × j + i × j, you are to find theM-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 ≤ MN × 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

题目意思是给出一个矩阵,这个矩阵中各个元素的值由给定的公式确定。即:i2 + 100000 ×i + j2 - 100000 × j + i × j。开始打了个10*10的表,以为矩阵元素得大小顺序是有规律的,交了后wa了,发现还是不能乱搞。从该式子可以看出,结果随着i单调递增,但对j呢?求导后可发现不是单调递增的,所以找规律就是错的。求第k大元素嘛,还是考虑二分。二分的关键是怎么去判断。因为j无单调性可言,那么就枚举j,二分找出每列的小于mid的个数cntx,和小于等于mid的个数cnty。总复杂度就是logINF*n*logn,是可以接受的。然后根据相应逻辑判断即可。这道题,就是二分里再套二分,还是蛮有意思的。

#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<iostream>
#include<map>
using namespace std;
typedef long long LL;
const int maxn = 1000 + 5;
const LL INF = 1LL<<40;
LL n,m;

LL cal(LL i,LL j){
    return  i*i + 100000 * i + j*j - 100000 * j + i * j;
}

int can(LL x){
    LL cntx = 0,cnty = 0;
    for(LL i = 1;i <= n;i++){
        LL l = 1,r = n;
        LL tem = -1;
        while(l <= r){
            LL mid = l+(r-l)/2;
            if(cal(mid,i) < x){
                l = mid+1;
                tem = mid;
            }
            else {
                r = mid-1;
            }
        }
        if(tem != -1){
            cntx += tem;
        }
    }
    for(LL i = 1;i <= n;i++){
        LL l = 1,r = n;
        LL tem = -1;
        while(l <= r){
            LL mid = l+(r-l)/2;
            if(cal(mid,i) <= x){
                l = mid+1;
                tem = mid;
            }
            else {
                r = mid-1;
            }
        }
        if(tem != -1){
            cnty += tem;
        }
    }
    if(cnty < m){
        return 1;
    }
    else if(cntx > m-1){
        return -1;
    }
    else{
        return 0;
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%I64d%I64d",&n,&m);
        LL l = -INF,r = INF;
        LL ans;
        while(l <= r){
            LL mid = l+(r-l)/2;
            int tag = can(mid);
            if(tag == 1){
                l = mid+1;
            }
            else if(tag == -1){
                r = mid-1;
            }
            else{
                ans = mid;
                break;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值