Matrix [POJ3685] [二分套二分]

Description

有一个N阶方阵 第i行,j列的值Aij =i2 + 100000 × i + j2 - 100000 × j + i × j,需要找出这个方阵的第M小值.

Input

第一行输入T代表测试组数.
每个测试用例包含2个数字N,M表示在N阶方阵找出第M大值, N(1 ≤ N ≤ 50,000) and M(1 ≤ M≤ N × N). 每两个测试用例之间可能有空行

Output

输出方阵的第M小值

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

Analysis

在二分m的大小之后,再次二分统计小于等于m的个数,但是可以有两个方向

我真的好蠢,调了一个下午...方向不太好

下面两个函数是等价的,一个关于i,一个关于j

f(i)=i2+(100000+j)×i+j2-100000×j 

f(j)=j2+(i-100000)×j+i2+100000×i)

我们发现第一个函数的对称轴小于0,也就是在[1,n]的区间上是单调的,而第二个函数对称轴是有可能在[1,n]区间里的,所以要分类讨论

我就是写的第二种...所以有一点...

Code

 1 #include<set>
 2 #include<map>
 3 #include<queue>
 4 #include<stack>
 5 #include<cmath>
 6 #include<cstdio>
 7 #include<cstring>
 8 #include<iostream>
 9 #include<algorithm>
10 #define RG register ll
11 #define rep(i,a,b)    for(RG i=a;i<=b;++i)
12 #define per(i,a,b)    for(RG i=a;i>=b;--i)
13 #define ll long long
14 #define inf (1<<29)
15 #define cal(x,y) (y*y+(x-100000ll)*y+x*x+100000ll*x)
16 using namespace std;
17 ll T;
18 ll n,m;
19 inline ll read()
20 {
21     ll x=0,f=1;char c=getchar();
22     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
23     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
24     return x*f;
25 }
26 
27 ll check(ll lim)
28 {
29     ll cnt=0;
30     rep(i,1,n)
31     {
32         ll AOF=(100000ll-i)/2ll;//对称轴
33         if(n<=AOF)
34         {
35             ll l=1,r=n,mid,num,ans=0;
36             while(l<=r)
37             {
38                 mid=l+r>>1,num=cal(i,mid);
39                 if(num>lim)    ans=mid,l=mid+1;
40                 else        r=mid-1;
41             }
42             cnt+=n-ans;
43         }
44         else
45         {
46             ll l=1,r=AOF,mid,num,ans=0;
47             while(l<=r)
48             {
49                 mid=l+r>>1,num=cal(i,mid);
50                 if(num>lim)    ans=mid,l=mid+1;
51                 else        r=mid-1;
52             }
53             cnt+=AOF-ans;//bug!
54             
55             l=AOF+1,r=n,mid,num,ans=AOF;
56             while(l<=r)
57             {
58                 mid=l+r>>1,num=cal(i,mid);
59                 if(num<=lim)    ans=mid,l=mid+1;
60                 else            r=mid-1;
61             }
62             cnt+=ans-AOF; 
63         }
64     }
65     return cnt>=m;
66 }
67 
68 int main()
69 {
70     T=read();
71     while(T--)
72     {
73         n=read(),m=read();
74         ll l=cal(1ll,n),r=max(cal(n,1ll),cal(n,n)),mid,ans;
75         while(l<=r)
76         {
77             mid=l+r>>1;
78             if(check(mid))    ans=mid,r=mid-1;
79             else            l=mid+1;
80         }
81         printf("%lld\n",ans);
82     }
83     return 0;
84 }
View Code
posted @ 2018-07-05 17:15  iBilllee  阅读(589)  评论(0编辑  收藏  举报