POJ2104 (平方分割)二分查找理解。
题意:任意区间求第k大数
思路:
预处理:利用平方分割(分桶法)把区间切割成B = sqrt(n)大小的一块块,然后每个各自排序。
二分第k大数x,接着就需要求[l,r]区间中x的排名,与k比较,将两边端点非完整桶的点进行扫描,最多B次,其余每个桶进行二分查找排名,可利用upper_bound(STL)即可快速实现。
评价:
二分确实坑爹,不过搞了这一题也算对二分查找理解深入了些。
二分正确做法:
对于[0..n-1)有[0..m]满足性质其余不满足, 则应用[l, r)进行二分查找, 最后l一定是正确的。总是保证l不成立,r成立。
l = -1, r = n; while(l < r-1) { int mid = (l+r)/2; if(check(mid)) l = mid; else r = mid; } cout << l << endl;
而若是[m, n-1]满足性质, 则应用(l, r]进行二分查找, 最后r一定正确,反之即可。
保证(l, r]正确性
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath> #include <utility> #include <vector> #include <queue> #include <map> #include <set> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 0x3f3f3f3f #define MAXN 200005 #define B 1000 using namespace std; vector<int> bucket[MAXN/B]; int a[MAXN], q[MAXN], n, m, x, y, k; int main() { scanf("%d%d", &n, &m); for(int i = 0; i < n; i ++) { scanf("%d", &a[i]); q[i] = a[i]; bucket[i/B].push_back(a[i]); } sort(q, q+n); for(int i = 0; i < MAXN/B; i ++) sort(bucket[i].begin(), bucket[i].end()); while(m --) { scanf("%d%d%d", &x, &y, &k); x--, y; int l = -1, r = n-1; //(l, r] while(l < r-1) { int mid = (l+r)/2; int tx = x, ty = y; int cnt = 0; for( ; tx < ty && tx%B != 0; tx ++) if(a[tx] <= q[mid]) cnt ++; for( ; tx < ty && ty%B != 0; ty --) if(a[ty-1] <= q[mid]) cnt ++; for(int i = tx/B; i < ty/B; i ++) cnt += upper_bound(bucket[i].begin(), bucket[i].end(), q[mid])-bucket[i].begin(); if(k <= cnt) r = mid; else l = mid; } if(r < 0) printf("-1"); else printf("%d\n", q[r]); } return 0; }
例如本题若换成[l, r)正确性,稍加改动即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath> #include <utility> #include <vector> #include <queue> #include <map> #include <set> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 0x3f3f3f3f #define MAXN 200005 #define B 1000 using namespace std; vector<int> bucket[MAXN/B]; int a[MAXN], q[MAXN], n, m, x, y, k; int main() { scanf("%d%d", &n, &m); for(int i = 0; i < n; i ++) { scanf("%d", &a[i]); q[i] = a[i]; bucket[i/B].push_back(a[i]); } sort(q, q+n); for(int i = 0; i < MAXN/B; i ++) sort(bucket[i].begin(), bucket[i].end()); while(m --) { scanf("%d%d%d", &x, &y, &k); x--, y; int l = 0, r = n; //[l, r) while(l < r-1) { int mid = (l+r)/2; int tx = x, ty = y; int cnt = 0; for( ; tx < ty && tx%B != 0; tx ++) if(a[tx] < q[mid]) cnt ++; for( ; tx < ty && ty%B != 0; ty --) if(a[ty-1] < q[mid]) cnt ++; for(int i = tx/B; i < ty/B; i ++) cnt += lower_bound(bucket[i].begin(), bucket[i].end(), q[mid])-bucket[i].begin(); if(cnt < k) l = mid; else r = mid; } printf("%d\n", q[l]); } return 0; }
那么多的束缚,我不曾放弃过;那么多的险阻,我不曾倒下过。