• POJ 2104 K-th Number ( 求取区间 K 大值 || 主席树 || 离线线段树)


    题意 : 给出一个含有 N 个数的序列,然后有 M 次问询,每次问询包含 ( L, R, K ) 要求你给出 L 到 R 这个区间的第 K 大是几

    分析 :

    求取区间 K 大值是个经典的问题,可以使用的方法有很多,我听过的只有主席树、整体二分法、划分树、分块……

    因为是看《挑战》书介绍的平方分割方法(分块),所以先把分块说了,其他的坑以后再填

    分块算法思想是将区间分为若干块,一般分为 n1/2 块然后在每块维护所需信息,可以把复杂度降到 O(根号n)

    具体的分析和代码在《挑战程序设计竞赛》有很详细的解释,这里说一下代码的实现细节

    题目在实现的时候用的是这种 [L, R) 左闭右开区间,这样的区间表示法在 STL 和 JAVA的类库中很常用

    这样有很多优点,其中一个优点就是区间的长度是L ~ R,而判断两个区间的交或者并的时候思考的难度也降低很多。

    L < R代表区间有值,L == R代表区间到了最后。用闭区间就特别麻烦,下面我给出的代码就是用闭区间的,纠结了我好久...

    #include<vector>
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int B = 1000;
    const int maxn = 1e5 + 10;
    vector<int> bucket[maxn / B];
    int num[maxn], arr[maxn];
    int N, M;
    
    int main(void)
    {
    
        while(~scanf("%d %d", &N, &M)){
            for(int i=0; i<N; i++){
                scanf("%d", &arr[i]);
                bucket[i / B].push_back(arr[i]);
                num[i] = arr[i];
            }
    
            sort(num, num + N);
            for(int i=0; i<N/B; i++)
                sort(bucket[i].begin(), bucket[i].end());
    
            int L, R, K;
            while(M--){
                scanf("%d %d %d", &L, &R, &K);
                L--, R--;
                int lb = 0, ub = N - 1, ans = -1;
                while(ub >= lb){
                    int mid = lb + ((ub - lb)>>1);
                    int c = 0;
                    int TL = L, TR = R;
                    while(TR+1 > TL && TL % B != 0) if(arr[TL++] <= num[mid]) c++;
                    while(TR+1 > TL && (TR+1) % B != 0) if(arr[TR--] <= num[mid]) c++;
    
                    while(TR >= TL){
                        c += upper_bound(bucket[TL/B].begin(), bucket[TL/B].end(), num[mid]) - bucket[TL/B].begin();
                        TL += B;
                    }
    
                    if(c >= K) ans = mid, ub = mid - 1;
                    else lb = mid + 1;
                }
                printf("%d
    ", num[ans]);
            }
        }
        return 0;
    }
    View Code

    2018-05-07 更新

    省赛被 可持久化Trie 打爆,决定学习一下可持久化数据结构

    学了主席树,离线求取 K 大值,注意一下离散化

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int maxn = 1e5 + 10;
    struct NODE{ int lc, rc, v; }Node[maxn*20];
    int root[maxn], sz;
    void Insert(int pre, int cur, int p, int l, int r)
    {
        if(l == r){
            Node[cur].v = Node[pre].v + 1;
            return ;
        }
    
        int m = l + ((r-l)>>1);
        if(p <= m){
            Node[cur].lc = ++sz;
            Node[cur].rc = Node[pre].rc;
            Insert(Node[pre].lc, Node[cur].lc, p, l, m);
        }else{
            Node[cur].rc = ++sz;
            Node[cur].lc = Node[pre].lc;
            Insert(Node[pre].rc, Node[cur].rc, p, m+1, r);
        }
    
        Node[cur].v = Node[Node[cur].lc].v + Node[Node[cur].rc].v;
    }
    
    int query(int L, int R, int l, int r, int k)
    {
        if(l == r) return l;
        int m = l + ((r-l)>>1);
        int tmp = Node[Node[R].lc].v - Node[Node[L].lc].v;
        if(tmp >= k)
            return query(Node[L].lc, Node[R].lc, l, m, k);
        else
            return query(Node[L].rc, Node[R].rc, m+1, r, k-tmp);
    }
    
    int arr[maxn];
    int mp[maxn];
    int main(void)
    {
        int N, M;
        scanf("%d %d", &N, &M);
        for(int i=0; i<N; i++)
            scanf("%d", &arr[i]),
            mp[i] = arr[i];
    
        sort(mp, mp+N);
        int len = unique(mp, mp+N) - mp;
    
        for(int i=1; i<=N; i++){
            int x = lower_bound(mp, mp+len, arr[i-1]) - mp;
            root[i] = ++sz;
            Insert(root[i-1], root[i], x+1, 1, len);
        }
    
        int i, j, k;
        while(M--){
            scanf("%d %d %d", &i, &j, &k);
            int ans = query(root[i-1], root[j], 1, len, k);
            printf("%d
    ", mp[ans-1]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    codeforces 466D
    codeforces 360B
    codeforces 383D
    codeforces 679B
    codeforces 571B
    codeforces 494B
    CodeForces 660D Number of Parallelograms(n个点所能组成的最多平行四边形数量)
    算法竞赛模板 最短路
    算法竞赛模板 string中substr函数的运用
    HDOJ 1269 迷宫城堡(tarjan模板题)
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/8318772.html
Copyright © 2020-2023  润新知