• poj 3685 Matrix 二分套二分 经典题型


    Matrix
    Time Limit: 6000MS   Memory Limit: 65536K
    Total Submissions: 5724   Accepted: 1606

    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

     
    题意:给你一个N*N的矩阵,第i行,j列的值为 i2 + 100000 × i + j2 - 100000 × j + i × j,求这些矩阵中的第m小的值。
     
    分析:暴力的话超时就不讲了,换到二分:首先分析 i2 + 100000 × i + j2 - 100000 × j + i × j 这个式子,可以发现当j固定时,整个式子的值是递增的,因此可以得出一个结论:在矩阵的同一列中,从上往下数值是递增的,因此可以利用二分枚举每一列的值。
       二分的核心思想:求数组中的第m小的数y,转换成求数组中<x的数量>=m的最小x,则y=x-1(待会证明), 所以该题二分套二分的核心思想就是:首先第第一层外层二分法枚举x,使得数组中<x的数量>=m(即合法),从而无限逼近直到得出最小的x,然后第二层内层层再用二分法求出在数组中<x的数量的个数,这个地方又是一个典型的二分,将求出在数组中<x的个数转换为求出值>=x的最小的下标p,那么<x的最大的下标q就是该下标减1即q=p-1了,然后统计个数就好。
        但是这两层二分都各有一个需要注意的问题:
        1.第一层二分中为什么y==x-1?因为假设y!=x-1,那么存在一个小于x的数x-1,使得
    <x-1的数量>=m与x是最小值矛盾,固得证。其实这也是整数性质的体现,因为可以这样看,求一个序列中的第m小的数值,则比该值+1的数值w(二分枚举出的,不管数组中存不存在),在数组中<w的数值是有m个的,那么w-1便是该第m小的数值。
        2.非常容易错!好好体会,见代码
    #include<cstdio>
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    #include <queue>
    #include <algorithm>
    #include <set>
    using namespace std;
    #define MM(a) memset(a,0,sizeof(a))
    typedef long long LL;
    typedef unsigned long long ULL;
    const int mod = 1000000007;
    const double eps = 1e-10;
    const int inf = 0x3f3f3f3f;
    long long mid,l,r,n,m;
    long long v(long long  i,long long j)
    {
        return i*i+100000*i+j*j-100000*j+i*j;
    }
    long long  ok(long long  x)
    {
        long long sum=0;
        for(long long  j=1;j<=n;j++)
          {
              long long  l=0,r=n+1; /*2.这个地方r不能初始化为n,因为假设
    n个数全是<x的话,按理讲应该sum+=n的,但是最后r-1之后sum+=(n-1)了,
    这是因为v(n,j)>x和v(n,j)=x的效果是不同的,假设v(n,j)>x的话,r仍然为n+1,sum+//=n,而当v(n,j)=x话,r更新为n,sum+=n-1;*/
              while(r-l>1)
              {
                   long long  mid=(l+r)>>1;
                   if(v(mid,j)>=x)
                         r=mid;
                   else
                         l=mid;
              }
              sum+=(r-1);
          }
        return sum;
    }
    int main()
    {
        int cas;
         scanf("%d",&cas);
        while(cas--)
        {
            scanf("%lld %lld",&n,&m);
            r=1e12;l=-1e12;
            while(r-l>1)
            {
                mid=(l+r)>>1;
                if(ok(mid)>=m)
                    r=mid;
                else
                    l=mid;
            }
            printf("%lld
    ",r-1);
        }
        return 0;
    }

      

  • 相关阅读:
    Android学习笔记之-Websercive 通讯
    Android学习笔记
    Android学习笔记-Android生成数字证书+签名
    jQuery教程总结
    SQL 数据库备份和恢复 镜像配置(证书方式)
    【网络部分总结的很好的帖子】方便以后找
    【动态规划】最长递增子序列
    【美团~牛客】十六进制转十进制
    【二分查找】及相关问题
    【动态规划】
  • 原文地址:https://www.cnblogs.com/smilesundream/p/5122960.html
Copyright © 2020-2023  润新知