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 }
越努力,越幸运