[hdu2665 Kth number]区间第k大数

题意:给n个数,m次询问(l, r, k),每次询问区间[l, r]内的第k大数是多少
想法:区间问题。
* 暴力做法,将数据提取出来,然后套 O(n) 的划分,总复杂度 O(mn)
* 在线段树上保存信息。比如归并树,保存归并排序的整个过程并对应到每个节点区间。或者像划分树那样,保存划分的结果,并维护一下进入到子区间的数的个数,这个信息可以在查询的时候用来确定第k大数被划到了左边还是右边。使用归并树复杂度为 O(nlogn+mlog3n) ,划分树复杂度为 O(nlogn+mlogn)
* 可持久化线段树,也被称之为主席树。对每个前缀区间建一棵线段树保存数字区间的出现次数和,那么任一个区间的信息就可以由两颗线段树相减得到,然后就是在线段树上二分了。。复杂度依然是 O(nlogn+mlogn) 。另外可持久化体现在节点的共用上,这里的实现比较巧妙,也是保证空间复杂度的关键。
* 如果这个题可以离线做,也可以考虑考虑莫队算法。需要用到一个支持插入、删除、求rank的数据结构,用平衡树的话复杂度为 O(nmlogn )

划分树:

#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#include "local.h"
#endif // ONLINE_JUDGE

#define pb(x)                   push_back(x)
#define mp(x, y)                make_pair(x, y)
#define all(a)                  (a).begin(), (a).end()
#define mset(a, x)              memset(a, x, sizeof(a))
#define mcpy(a, b)              memcpy(a, b, sizeof(b))
#define up(a, b)                for (int a = 0; a < (b); a ++)
#define down(a, b)              for (int a = b - 1; (a) >= 0; a --)
#define rep(i, a, b)            for (int i = a; i <= (b); i ++)
#define rrep(i, a, b)           for (int i = a; i >= (b); i --)
#define cas()                   int T, cas = 0; cin >> T; while (T --)
#define printCas(ch)            printf("Case #%d:%c", ++ cas, ch)
#define watch(ele)              cout << ele << endl;
#define in(a)                   scanf("%d", &a)

typedef long long ll;
typedef pair<int, int> pii;

template<typename T>bool umax(T&a, const T&b){return a<b?(a=b,true):false;}
template<typename T>bool umin(T&a, const T&b){return b<a?(a=b,true):false;}

const int N = 1e5 + 7;

struct SegTree {
    int sum[20][N], seq[20][N];
    void init(int a[]) {
        memcpy(seq[0], a, sizeof(seq[0]));
    }
    void build(int b[], int d, int l, int r) {
        if (l == r) return;
        int m = l + r >> 1, mid = b[m], cnt = l;
        rep(i, l, r) if (seq[d][i] < mid) cnt ++;
        int tl = l, tr = m + 1;
        rep(i, l, r) {
            int val = seq[d][i];
            sum[d][i] = sum[d][i - 1];
            if (val < mid) seq[d + 1][tl ++] = val, sum[d][i] ++;
            if (val > mid) seq[d + 1][tr ++] = val;
            if (val == mid) {
                if (cnt <= m) seq[d + 1][tl ++] = val, sum[d][i] ++, cnt ++;
                else seq[d + 1][tr ++] = val;
            }
        }
        build(b, d + 1, l, m);
        build(b, d + 1, m + 1, r);
    }
    int query(int L, int R, int k, int d, int l, int r) {
        if (l == r) return seq[d][l];
        int m = l + r >> 1, cr = sum[d][R] - sum[d][l - 1], cl = sum[d][L - 1] - sum[d][l - 1], buf = cr - cl;
        if (buf >= k) return query(l + cl, l + cr - 1, k, d + 1, l, m);
        else return query(m + 1 + L - l - cl, m + R - l + 1 - cr, k - buf, d + 1, m + 1, r);
    }
};
SegTree st;
int n, m, s, t, k, a[N], b[N];
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    cas() {
        cin >> n >> m;
        up(i, n) in(a[i + 1]);
        mcpy(b, a);
        sort(b + 1, b + 1 + n);
        st.init(a);
        st.build(b, 0, 1, n);
        while (m --) {
            in(s); in(t); in(k);
            printf("%d\n", st.query(s, t, k, 0, 1, n));
        }
    }
    return 0;
}

主席树:

#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#include "local.h"
#endif // ONLINE_JUDGE

#define pb(x)                   push_back(x)
#define mp(x, y)                make_pair(x, y)
#define all(a)                  (a).begin(), (a).end()
#define mset(a, x)              memset(a, x, sizeof(a))
#define mcpy(a, b)              memcpy(a, b, sizeof(b))
#define up(a, b)                for (int a = 0; a < (b); a ++)
#define down(a, b)              for (int a = b - 1; (a) >= 0; a --)
#define rep(i, a, b)            for (int i = a; i <= (b); i ++)
#define rrep(i, a, b)           for (int i = a; i >= (b); i --)
#define cas()                   int T, cas = 0; cin >> T; while (T --)
#define printCas(ch)            printf("Case #%d:%c", ++ cas, ch)
#define watch(ele)              cout << ele << endl;
#define in(a)                   scanf("%d", &a)

typedef long long ll;
typedef pair<int, int> pii;

template<typename T>bool umax(T&a, const T&b){return a<b?(a=b,true):false;}
template<typename T>bool umin(T&a, const T&b){return b<a?(a=b,true):false;}

const int N = 1e5 + 7;

struct SegTree {
    struct Node {
        int lp, rp, sum;
    };
    int c;
    Node node[N * 22];
    void clear() {
        c = 0;
        mset(node, 0);
    }
    void build(int l, int r, int &rt) {
        rt = c ++;
        if (l == r) return;
        int m = l + r >> 1;
        build(l, m, node[rt].lp);
        build(m + 1, r, node[rt].rp);
    }
    void update(int x, int v, int last, int l, int r, int &rt) {
        rt = c ++;
        node[rt] = node[last];
        node[rt].sum += v;
        if (l == r) return;
        int m = l + r >> 1;
        if (x <= m) update(x, v, node[last].lp, l, m, node[rt].lp);
        else update(x, v, node[last].rp, m + 1, r, node[rt].rp);
    }
    int query(int s, int t, int k, int l, int r) {
        if (l == r) return l;
        int m = l + r >> 1, buf = node[node[t].lp].sum - node[node[s].lp].sum;
        if (buf >= k) return query(node[s].lp, node[t].lp, k, l, m);
        else return query(node[s].rp, node[t].rp, k - buf, m + 1, r);
    }
};
SegTree st;
int n, m, s, t, k, sz, a[N], b[N], root[N];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    cas() {
        cin >> n >> m;
        up(i, n) in(a[i]);
        up(i, n) b[i] = a[i];
        sort(b, b + n);
        sz = unique(b, b + n) - b;
        up(i, n) a[i] = lower_bound(b, b + sz, a[i]) - b;
        st.clear();
        st.build(0, sz - 1, root[0]);
        up(i, n) st.update(a[i], 1, root[i], 0, sz - 1, root[i + 1]);
        while (m --) {
            in(s); in(t); in(k);
            printf("%d\n", b[st.query(root[s - 1], root[t], k, 0, sz - 1)]);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值