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;
}
View Code

 

例如本题若换成[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;
}
View Code

 

posted @ 2014-12-10 15:52  Estimator  阅读(457)  评论(0编辑  收藏  举报