• b_vj_K-th Number(二分+线段树)


    有一个数列a[1~N]中 (数字各不相同),输入m行i,j,k,目的是求a[i...j]之间第K小的数

    思路:二分+线段树:二分枚举数字num,然后在线段树的每个区间中找小于num的个数c,根据c待定二分边界

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e6+5;
    ll n,m,a[N];
    vector<ll> t[N];
    void pushup(int k) {
        merge(t[k<<1].begin(), t[k<<1].end(), t[k<<1|1].begin(), t[k<<1|1].end(), t[k].begin());
    }
    void build(int l, int r, int k) {
        if (l==r) {
            t[k].push_back(a[l]);
            return;
        }
        int m=l+r>>1;
        build(l,m,k<<1);
        build(m+1,r,k<<1|1);
        t[k].resize(r-l+1);
        pushup(k);
    }
    int ask(int ql, int qr, int l, int r, int k, ll num) { //求ql,qr中找有多少个数字小于num
        if (ql<=l && r<=qr) {
            return upper_bound(t[k].begin(), t[k].end(), num)-t[k].begin(); //大于num的第一个位置,能到num
        }
        int m=l+r>>1, ans=0;
        if (m>=ql) ans+=ask(ql,qr,l,m,k<<1,num);
        if (m<qr)  ans+=ask(ql,qr,m+1,r,k<<1|1,num);
        return ans;
    }
    ll b_search(int s, int e, int k) {
        ll l=-1e9, r=1e9+1;
        while (l<r) {
            ll m=l+r>>1;
            if (ask(s,e,1,n,1,m)<k) l=m+1;
            else r=m;
        }
        return l;
    }
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        cin>>n>>m; for (int i=1; i<=n; i++) cin>>a[i];
        build(1,n,1);
        for (int i=0; i<m; i++) {
            int l,r,k; cin>>l>>r>>k;
            cout<<b_search(l,r,k)<<'
    ';
        }
        return 0;
    }
    

    建树复杂度:遍历了logn层,每层的merge函数累积遍历n个元素
    查询复杂度:mloglog(e-s)

    感觉比按双字段排序还要慢一点...

  • 相关阅读:
    Ubuntu18.04安装配置
    Ubuntu Terminal「控制台」
    Ubuntu16.04系统安装
    UEFI Install CentOS 7
    安装Ubuntu16.04后要做的事
    Ubuntu Google Chrome
    Nuke Linux Crack
    Selenium3自动化测试实战 基于Python语言
    函数基础
    Jenkins持续集成
  • 原文地址:https://www.cnblogs.com/wdt1/p/13929905.html
Copyright © 2020-2023  润新知