主席树模板

主席树模板 I - 静态区间第k大

Face


题意

  • 静态区间第k大

数据范围: 1 ≤ n , m ≤ 2 5 , ∣ a [ i ] ∣ ≤ 1 0 9 1\leq n ,m\leq 2^5 , |a[i]| \leq 10^9 1nm25,a[i]109


前置技能
  • 线段树
Tutorial:
查找更新时空复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
总空间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

code:


#include <bits/stdc++.h>

using namespace std;

#define local
#ifdef local

template<class T>
void _E(T x) { cerr << x; }

void _E(double x) { cerr << fixed << setprecision(6) << x; }

void _E(string s) { cerr << "\"" << s << "\""; }

template<class A, class B>
void _E(pair<A, B> x) {
    cerr << '(';
    _E(x.first);
    cerr << ", ";
    _E(x.second);
    cerr << ")";
}

template<class T>
void _E(vector<T> x) {
    cerr << "[";
    for (auto it = x.begin(); it != x.end(); ++it) {
        if (it != x.begin()) cerr << ", ";
        _E(*it);
    }
    cerr << "]";
}

void ERR() {}

template<class A, class... B>
void ERR(A x, B... y) {
    _E(x);
    cerr << (sizeof...(y) ? ", " : " ");
    ERR(y...);
}

#define debug(x...) do { cerr << "{ "#x" } -> { "; ERR(x); cerr << "}" << endl; } while(false)
#else
#define debug(...) 114514.1919810
#endif
#define _rep(n, a, b) for (ll n = (a); n <= (b); ++n)
#define _rev(n, a, b) for (ll n = (a); n >= (b); --n)
#define _for(n, a, b) for (ll n = (a); n < (b); ++n)
#define _rof(n, a, b) for (ll n = (a); n > (b); --n)
#define oo 0x3f3f3f3f3f3f
#define ll long long
#define db long double
#define eps 1e-3
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<ll, ll>
#define pdd pair<db, db>
#define endl "\n"
//#define ls ch[now][0]
//#define rs ch[now][1]
const ll mod = 998244353;
const ll maxn = 2e5 + 10;


ll qpow(ll a, ll b) {
    ll ret = 1;
    for (; b; a = a * a % mod, b >>= 1) {
        if (b & 1) {
            ret = ret * a % mod;
        }
    }
    return ret;
}

vector<ll> f(maxn), invf(maxn);

ll inv(ll a) {
    return qpow(a, mod - 2);
}

void prework() {
    f[0] = 1;
    _rep(i, 1, maxn - 1) {
        f[i] = f[i - 1] * i % mod;
    }
    invf[maxn - 1] = qpow(f[maxn - 1], mod - 2);
    for (ll i = maxn - 2; i >= 0; i--) {
        invf[i] = invf[i + 1] * (i + 1) % mod;
    }

}

ll C(ll n, ll m) {
    if (n > m || m < 0)return 0;
    if (n == 0 || m == n) return 1;
    ll res = (f[m] * invf[m - n] % mod * invf[n]) % mod;
    return res;
}


const ll M = (2e5) + 10;
#define ls tree[now].l
#define rs tree[now].r

ll n, m;
ll k, N;
ll root[M];
ll a[M], ys[M];
//a存离散化后的数,ys建立离散化后与原数的映射关系,root为森林的不同根节点

struct Node {
    ll num;
    ll id;
} q[M];

struct node {
    ll l, r;
    ll size;
} tree[M << 5];//多开 1<<5 的空间

inline ll read() {
    ll f = 1, x = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

bool cmp(Node x, Node y) {//求第k小的数,所以从小到大排序
    return x.num < y.num;
}

void lsh() {//离散化模板
    a[q[1].id] = ++N;
    ys[N] = q[1].num;//注意存映射的点
    for (ll i = 2; i <= n; i++) {
        if (q[i].num != q[i - 1].num) N++;
        a[q[i].id] = N;
        ys[N] = q[i].num;
    }
}

ll build(ll l, ll r) {//动态建树,防止数组越界或开不够
    ll now = ++k;//加点
    if (l < r) {
        ll mid = (l + r) >> 1;
        ls = build(l, mid);//更新左儿子
        rs = build(mid + 1, r);//更新右儿子
    }
    return now;
}

ll update(ll pre, ll l, ll r, ll x) {//建权值线段树
    ll now = ++k;
    tree[now].size = tree[pre].size + 1;//此线段树的点数为上一棵的点数+1
    ls = tree[pre].l;
    rs = tree[pre].r;
    //继承上一棵线段树
    if (l < r) {//寻找需要更新的链
        ll mid = (l + r) >> 1;
        if (x > mid) rs = update(rs, mid + 1, r, x);//在右儿子更新
        else
            ls = update(ls, l, mid, x);//在左儿子更新
    }
    return now;
}

ll query(ll u, ll v, ll l, ll r, ll x) {//查询操作
    if (l == r) return l;//找到第k小值
    //第r棵线段树左儿子-第(l-1)棵线段树左儿子的值
    ll t = tree[tree[v].l].size - tree[tree[u].l].size;
    ll mid = (l + r) >> 1;
    if (x <= t) return query(tree[u].l, tree[v].l, l, mid, x);//在左儿子上
    else return query(tree[u].r, tree[v].r, mid + 1, r, x - t);//在右儿子上
}

signed main() {
    n = read();
    m = read();
    for (ll i = 1; i <= n; i++) {
        q[i].num = read();
        q[i].id = i;
    }
    sort(q + 1, q + n + 1, cmp);
    lsh();
    root[0] = build(1, N);//建一颗空的线段树
    for (ll i = 1; i <= n; i++)//建n棵线段树,边加点边建树
        root[i] = update(root[i - 1], 1, N, a[i]);
    for (ll i = 1, r; i <= m; i++) {
        r = read();
        int l = 1;
        int k = i;
        printf("%lld\n", ys[query(root[l - 1], root[r], 1, N, k)]);
        //[l,r]就等价于 第r棵线段树-第(l-1)棵线段树 的k小值,返回该节点映射的值
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值