POJ-3579 Median---二分第k大(二分套二分)

题目链接:

https://cn.vjudge.net/problem/POJ-3579

题目大意:

求的是一列数所有相互之间差值的序列的最中间的值是多少。

解题思路:

可以用二分套二分的方法求解第m大,和POJ-3685类似,这里的模板也差不多

枚举第m大x,判断小于等于x的数目是不是大于m,如果大于m说明x >= 第m大,调整区间r = mid - 1

不然l = mid + 1

此处是大于m而不是小于m是因为一个数可能出现多次,那么小于等于中间的数目可能就比m大

在计算小于等于x的数目的时候,用upperlower函数求解即可

一开始TLE是因为左右区间没选好,应该选给定的数字中的最大值减最小值作为右区间

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 typedef long long ll;
 8 const ll INF = 1e9 + 7;
 9 const int maxn = 1e6 + 10;
10 ll a[maxn], n, m;
11 
12 ll ok(ll mid)
13 {
14     ll ans = 0;
15     for(int i = 1; i < n; i++)//此处不等于n是由于到了n没有比它更大的了,加上=也无妨
16     {
17         if(a[i] + mid >= a[n])//可以加速一点
18         {
19             ans += n - i;
20             continue;
21         }
22         int t = upper_bound(a + i, a + n + 1, a[i] + mid) - a;//这里用区间a+i而不是a+1可以加速求解
23         ans += t - 1 - i;
24     }
25     return ans;
26 }
27 int main()
28 {
29     while(scanf("%lld", &n) != EOF)
30     {
31         for(int i = 1; i <= n; i++)scanf("%lld", &a[i]);
32         sort(a + 1, a + n + 1);
33         m = n * (n - 1) / 2;
34         m = (m + 1) / 2;//求出第m个
35         ll l = 0, r = a[n] - a[1], ans;
36         while(l <= r)
37         {
38             ll mid = (l + r) / 2;
39             if(ok(mid) >= m)//小于等于mid的数字个数 >= m 说明mid>=最优解
40             {
41                 ans = mid;
42                 r = mid - 1;
43             }
44             else l = mid + 1;
45         }
46         printf("%lld\n", ans);
47     }
48     return 0;
49 }

 

posted @ 2018-05-10 23:43  _努力努力再努力x  阅读(168)  评论(0编辑  收藏  举报