zoukankan      html  css  js  c++  java
  • 【转】poj 2104 Kth Number

    二分查找 + 线段树
    这里线段树就是query()函数。不懂线段树的童鞋先不要急,你们姑且把它看成是二分就行了。
    二分在这道题中是很重要的思想. 先初始化, b[]数组排好序就是a[0][], x=0, y=n-1. 我们先取中间的数num=a[0][mid], where mid=(x+y)/2 来测试, 我们得到num在b[lef...rig]中的排在[rankL, rankR)的位置(注意是左闭右开区间). 如果
    A. rankL<=k && k<rankR 那么数num就是所求的数; 否则根据下面更新x或y不断迭代.
    B.k >= rankR 那么num较小, 也就是mid的位置较低, 于是取[x...y]区间的右边, 赋值x = mid+1;
    C. y = mid; 那么num较大, 于是取[x...y]区间的左边, 赋值y = mid;
     
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
     
    const int ex = 20;
    const int maxn =100104;
    // rankL是指示大于等于某数的最小位置, rankR是指示大于某数的最小位置
    // 比如在数组(必须升序)1, 3, 3, 5, 6, 查找数3, 那么rankL=1, rankR=3; 查找数4, 那么rankL=rankR=3.
    int n, m, lef, rig, k, rankL, rankR; // 在每个查询中, 查询区间b[lef...rig]的第k个数.
    // a数组共有ex层, 在每一层内, 我们只对某一段进行排序. 看level_sort()函数对其进行理解.
    int a[ex][maxn], b[maxn];
     
    // 提示: 把level_sort改为归并排序可以节省时间
    void level_sort(int x, int y, int dep) { // 表示在层dep中, 我们对a[dep][x...y]进行排序.
    if (x == y) return; // 只有一个数, 直接返回; 否则我们需要深入下一层继续排序
    sort(a[dep]+x, a[dep]+y+1);
    int mid = (x+y) / 2;
    level_sort(x, mid, dep+1);
    level_sort(mid+1, y, dep+1);
    }
     
    // 预处理
    void process() {
    for (int i=0; i < ex; i++) {
    memcpy(a[i], b, sizeof(int)*n);
    }
    level_sort(0, n-1, 0);
    }
     
    // binary search, 二分查找.
    // 可以把它看成是两个函数合成一个, 用judge来判断.
    // if (judge == 0), 那么就返回大于等于某数的最小位置(和rankL相关)
    // if (judge == 1), 那么就返回大于某数的最小位置(和rankR相关)
    int bin(int x, int y, int num, int dep, int judge) {
    y++;
    int ll = x;
    while (y > x) {
    int mid = (x+y) / 2;
    if (a[dep][mid] - num >= judge) y = mid;
    else x = mid+1;
    }
    return x-ll;
    }
     
    // 线段树, 相当于二分. 功能: 求数num在b[l...r]中的位置[rankL, rankR)
    // 我们现在处于a[dep][ll...rr]中.
    void query(int ll, int rr, int l, int r, int num, int dep) {
    if (ll==l && rr==r) { // 因为a[dep][ll...rr]是排好序的, 而我们需要查找num在b[l(=ll)...r(=rr)]的位置, 所以就可以直接二分查找求位置了.
    rankL += bin(ll, rr, num, dep, 0);
    rankR += bin(ll, rr, num, dep, 1);
    return;
    }
    // 否则, 我们需要深入到下一层迭代.
    if (r<ll || l>rr) return;
    int mid = (ll+rr) / 2;
    query(ll, mid, l, min(mid, r), num, dep+1);
    query(mid+1, rr, max(l, mid+1), r, num, dep+1);
    }
     
    void solve() {
    int x = 0;
    int y = n-1;
    int mid = (x+y) / 2;
    while (true) {
    mid = (x+y) / 2;
    rankL=0, rankR=0;
    query(0, n-1, lef, rig, a[0][mid], 0); // 求数a[0][mid]的位置[rankL, rankR)
    if (rankL<=k && k<rankR) break; // 数a[0][mid]就是所求的数
    if (k >= rankR) x = mid+1; // a[0][mid]较小, 也就是mid的位置较低, 于是取[x...y]区间的右边, 继续迭代
    else y = mid; // a[0][mid]较大, 也就是mid的位置较高, 于是取[x...y]区间的左边, 继续迭代
    }
    printf("%d\n", a[0][mid]);
    }
     
    int main()
    {
    scanf("%d%d", &n ,&m);
    for (int i=0; i < n; i++) {
    scanf("%d", &b[i]);
    }
    process();
    while (m--) {
    scanf("%d%d%d", &lef, &rig, &k);
    lef--; rig--; k--;
    solve();
    }
    return 0;
    }
     
     

    有一年多没发过博文了,刚到自己的博客里看,居然几天前还有访客。并且两个月前到现在的访问量还蛮稳定的,说明我写的东西还是能让一些读者感到有兴趣的(大部分应该是计算机专业的学生),同时希望大家能从我写的内容学到一些东西。:-)

    至于为什么我在一年多后的今天再次写博文,是因为POJ 2104这道题有些触动到我,所以我想写下一些东西和大家分享。它触动我的原因首先是这道题很好,另外这道题让我调试了一天,而错误的地方是在solve()函数里,那个while(true)循环原本我写成是while(y > x)导致WA;读者可以自己思考错的原因是什么,并且欢迎你们把自己的想法写下来回复我。
     
  • 相关阅读:
    2017.5.5上午学习内容
    2017.5.4下午学习内容
    2017.5.4上午学习内容
    2017.5.3上午学习内容
    2017.4.28下午学习内容
    2017.4.28上午学习内容
    scrapy项目的代码书写流程
    pycharm的远程连接
    mongodb的安装---linux篇
    mongo的安装和使用---windows篇
  • 原文地址:https://www.cnblogs.com/lzhitian/p/2618093.html
Copyright © 2011-2022 走看看