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 }

原文地址:https://www.cnblogs.com/fzl194/p/9022437.html

时间: 2024-11-08 19:36:09

POJ-3579 Median---二分第k大(二分套二分)的相关文章

POJ 3579 Median 二分+思维

POJ 3579 Median 二分+思维 题意 给你一些数,然后求这些数相互之间差的绝对值,然后绝对值排序,找到中间那个数. 解题思路 我反正一直开始是没有想到这个题竟然可以用二分来做.━━( ̄ー ̄*|||━━. 二分枚举答案,假设枚举值为mid,然后就是在排好序的序列中对每一个num[i]找到在i之后,有多少个大于num[i]+mid的数的个数(数列里的值比num[i]+mid大,说明该值与num[i]作差形成的新数列里的数比中位数mid大),用lower_bound计算所有差值比mid大于

poj 3579 Median 二分查找与lower_bound

题意: 给n个数,他们两两之间较大数减去较小数总共有n*(n-1)/2个数,要求这些数的中位数. 分析: 两次二分,第一次枚举答案,第二次判断该数是否可能成为中位数. 代码: //poj 3579 //sep9 #include <iostream> #include <algorithm> using namespace std; const int maxN=1e5+10; int a[maxN]; int n,m; int test(int x) { int sum=0; f

51 NOD 1685 第K大区间2 二分+BIT

题目描述: 定义一个长度为奇数的区间的值为其所包含的的元素的中位数. 现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少. 样例解释: [l,r]表示区间的值 [1]:3 [2]:1 [3]:2 [4]:4 [1,3]:2 [2,4]:2 第三大是2 输入: 第一行两个数n和k(1<=n<=100000,k<=奇数区间的数量) 第二行n个数,0<=每个数<2^31 输出: 一个数表示答案. 题解: 二分答案t,统计中位数大于等于t的区间有多少个. 设a[i]为

ACM学习历程—51NOD 1685 第K大区间2(二分 &amp;&amp; 树状数组 &amp;&amp; 中位数)

http://www.51nod.com/contest/problem.html#!problemId=1685 这是这次BSG白山极客挑战赛的E题. 这题可以二分答案t. 关键在于,对于一个t,如何判断它是否能成为第k大. 将序列中大于t的置为1,小于t的置为-1,等于t的置为0.那么区间中位数大于t的和就大于0,小于t的就小于0.于是就是判断区间和大于0的个数是否小于等于k. 维护前缀和sum(i),然后统计之前sum(j)小于sum(i)的有多少个,就是以i为右值的区间和大于0的个数.于

ZOJ 1112 Dynamic Rankings【动态区间第K大,整体二分】

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1112 题意: 求动态区间第K大. 分析: 把修改操作看成删除与增加,对所有操作进行整体二分. 代码: #include<cstdio> #include<iostream> #include<cstring> using namespace std; #define pr(x) cout << #x << &quo

静态区间第k大 树套树解法

然而过不去你谷的模板 思路: 值域线段树\([l,r]\)代表一棵值域在\([l,r]\)范围内的点构成的一颗平衡树 平衡树的\(BST\)权值为点在序列中的位置 查询区间第\(k\)大值时 左区间在\([l,r]\)范围内的树的大小与\(k\)比较 大了进去,小了减掉换一边 关于建树 递归建估计是\(O(nlog^2n)\)的 Code: #include <cstdio> #include <cstdlib> #include <algorithm> #includ

poj 3579 Median (二分搜索之查找第k大的值)

Description Given N numbers, X1, X2, ... , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly

POJ 3579 Median(二分答案+Two pointers)

[题目链接] http://poj.org/problem?id=3579 [题目大意] 给出一个数列,求两两差值绝对值的中位数. [题解] 因为如果直接计算中位数的话,数量过于庞大,难以有效计算, 所以考虑二分答案,对于假定的数据,判断是否能成为中位数 此外还要使得答案尽可能小,因为最小的满足是中位数的答案,才会是原差值数列中出现过的数 对于判定是不是差值的中位数的过程,我们用尺取法实现. 对于差值类的题目,还应注意考虑边界,即数列只有一位数的情况. [代码] #include <cstdio

POJ - 3579 Median 二分

题目大意:给出n个数,要求将这n个数两两相减,把这些相减得到的数排序后,输出位置在中间的那个数 解题思路:如果两两相减再排序复杂度太高,肯定超时了,不妨换另一种思路 枚举最中间的那个数,然后判断一下相减得到的数有多少个大于等于枚举的数 如何判断上面所说的那句呢,其实不用把每个数相减,只需要排序一下,然后将当前这个数 + 枚举的那个数,然后在数组中找到大于等于这个数的第一个位置(lower_bound()),再用n减去那个数的位置就可以知道有多少个相减的结果会大于等于这个数了 最后只需要判断一下大

第k大的数,前k大的数

1.排序后去出前k个,o(n*log(n))    如果k<log(n),可以考虑直接选择排序,因为只需要执行找到第k个就可以结束 o(n*k) 2.o(nlog(k))快排把数分为了两个部分,所以考虑两个情况,如果大的部分的个数>k,说明只要继续在大的部分找就可以了, 如果大的部分的个数<k,先把这些数取了,然后继续在小的部分里面找剩下的数(k-大的部分的个数)就可以了. 3.o(nlog((maxv-minv)/delta)),平均为o(nlogn)   转化为找第k个,  假设最大