spoj K-th Number (classic! 区间静态第k大)

这道题的 comment 里有人说
题目链接

Beautiful problem. There exists solution which run in O(log^3 N), O(log^2 N), and O(log N) per query. Explore them and learn something new!

我学到的第一种应该是最大的那种, 就是构造一个线段树, 其中每个节点都是排序后的对应区间上的数。然后通过二分求第k大
这种只能处理静态。。

const int N = 100005;
int n, m, ai[N];
vector<int> tree[N*4], nodes;
#define lc o<<1
#define rc o<<1|1

void build(int o, int l, int r) {
    if ( l == r ) {
        int x;
        scanf("%d", &x);
        tree[o].push_back(x);
    } else {
        int m = (l + r) >> 1;
        build(lc, l, m);
        build(rc, m + 1, r);
        tree[o].resize(r - l + 1);
        merge(tree[lc].begin(), tree[lc].end(),
              tree[rc].begin(), tree[rc].end(), tree[o].begin());
    }
}

void query(int o, int l, int r, int ql, int qr) {
    if ( ql <= l && r <= qr ) {
        nodes.push_back(o);
        return;
    }
    int m = (l + r) >> 1;
    if ( ql <= m )
        query(lc, l, m, ql, qr);
    if ( qr > m )
        query(rc, m + 1, r, ql, qr);
}

int cnt(int key) {
    int ret = 0;
    for (int i = 0; i < nodes.size(); ++i) {
        const vector<int> &v = tree[nodes[i]];
        ret += lower_bound(v.begin(), v.end(), key) - v.begin();
    }
    return ret;
}

int main() {
#ifdef _LOCA_ENV_
    freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
    scanf("%d%d", &n, &m);
    build(1, 1, n);
    vector<int> vals = tree[1];

    vals.resize( unique(vals.begin(), vals.end()) - vals.begin() );

    rep(ri, 1, m) {
        int x, y, k;
        scanf("%d%d%d", &x, &y, &k);
        int l = 0, r = vals.size(), m;
        nodes.clear();
        query(1, 1, n, x, y);
        // 这里的复杂度
        // logn * logn * logn
        while ( l < r ) {
            m = (l + r) >> 1;
            int tmp = cnt(vals[m]);
            if ( tmp < k )
                l = m + 1;
            else
                r = m;
        }
        printf("%d\n", vals[r-1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值