K-th Number (Segment Tree)

SPOJ.com - Problem MKTHNUM
2104 -- K-th Number

  • segment tree による手法だと一発で AC した。(SPOJ)
  • メモリをたくさん使う分やはり速い。
  • merge は std::back_inserter を使ってもよいが事前に resize しておくほうが速いはず。
  • バケット法はこちらK-th Number - shifth’s blog
class KthNumber {
public:
    // セグメント木による手法
    // 各ノードに数列をもたせる。
    struct SegTree {
        vector<vector<int>> data;
        SegTree(const vector<int> &a) {
                int k = 1;
                // 一番下の段が a を含むようにする
                while (k < (int)a.size()) k <<= 1;
                data.resize(2*k); // 2*(最下段の長さ) が SegTree の必要メモリサイズ
                init(a, 0, 0, k); // 一番上の段は [0,k) に対応
        }
        // <--------- log(K) ----------->
        // 1*K + 2*(K/2) + 4*(K/4) + ...  = O(K*log(K))
        // memory used : O(K*log(K))
        //
        // data[k] ノードが [l,r) を表現する
        void init(const vector<int> &a, int k, int l, int r) {
                if (r-l == 1) // 葉
                {
                    if (l < (int)a.size())
                        data[k].push_back(a[l]);
                }
                else
                {
                    int lch = k*2+1;
                    int rch = k*2+2;
                    init(a, lch, l, (l+r)/2);
                    init(a, rch, (l+r)/2, r);
                    data[k].resize(data[lch].size()+data[rch].size()); // merge 前にメモリ確保
                    merge(RANGE(data[lch]), RANGE(data[rch]), data[k].begin());
                }
        }
        //  O(log^2(K))
        // [i,j) 内の x の数を数える
        int query(int i, int j, int x,   // query 情報
                  int k, int l, int r) { // node の情報
                if (j <= l || r <= i)
                    return 0;
                else if (i <= l && r <= j) // query 区間がノード管理区間にすっぽり含まれるとき
                    return upper_bound(RANGE(data[k]), x)-data[k].begin();
                else
                {
                    int lc = query(i, j, x, k*2+1, l, (l+r)/2);
                    int rc = query(i, j, x, k*2+2, (l+r)/2, r);
                    return lc+rc;
                }
        }
        int query(int i, int j, int x) { return query(i, j, x, 0, 0, data.size()/2); }
    };
    // O(n*log(n)+m*log^3(n))
    void solve_segtree() {
            int n,m;
            cin>>n>>m;
            vector<int> a(n);
            vector<int> sorted_a(n); // sort 済み a
            REP(i,n)
            {
                cin>>a[i];
                sorted_a[i] = a[i];
            }
            sort(RANGE(sorted_a));
            // sort 済み配列から segtree を構築
            SegTree sgt(a);

            // O(m*log^3(n))
            REP(_,m)
            {
                int l,r,k;
                cin>>l>>r>>k;
                --l;
                int lb = -1;
                int ub = a.size()-1;

                // binary search
                while (ub-lb > 1)
                {
                    int md = (lb+ub)/2;
                    int x = sorted_a[md];
                    int c = sgt.query(l,r,x);
                    if (c >= k)
                        ub = md;
                    else
                        lb = md;
                }
                cout<<sorted_a[ub]<<endl;
            }
    }
    void solve() {
            // solve_bucket();
            solve_segtree();
    }
 };