• POJ 2104 HDU 2665 主席树 解决区间第K大


    两道题都是区间第K大询问,数据规模基本相同。

    解决这种问题, 可以采用平方划分(块状表)复杂度也可以接受,但是实际表现比主席树差得多。

    这里大致讲一下我对主席树的理解。

    首先,如果对于某个区间【L,R】,对这个区间排序后,我们构造出了线段树, 每个值在这个子区间出现了几次都可以从线段树中查询,我们就可以在对数时间解决询问。

    现在的问题就是怎样在时空复杂度O(nlogn)的时间内构造出这么多的线段树!


    1.首先对原序列a排序并删去重复得到新序列b 

    2.对a的每个前缀构造线段树,即a的前i个数出现在b的[L,R]中的个数。

    3.相邻前缀的线段树只有一条边不同,故每次只需要增加logn个点,总的空间复杂度O(nlogn)

    4.前缀线段树的可加性,rt[r]-rt[l-1]就表示了原序列a[l,r]在新序列b[L,R]的数量。

    代码很清晰,参照代码和上述4个性质,应该可以很快理解主席树。

    一句话总结一下, 在本题中, 主席树就的功能就是快速的告诉我们:

      原序列a的子序列a[l,r]在a排序后的序列b的子序列[L,R]中的个数。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N = 100000 + 5;
    
    int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20];
    
    int n, k, tot, sz, ql, qr, x, q, T;
    
    void Build(int& o, int l, int r){
        o = ++ tot;
        sum[o] = 0;
        if(l == r) return;
        int m = (l + r) >> 1;
        Build(ls[o], l, m);
        Build(rs[o], m + 1, r);
    }
    
    void update(int& o, int l, int r, int last, int p){
        o = ++ tot;
        ls[o] = ls[last];
        rs[o] = rs[last];
        sum[o] = sum[last] + 1;
        if(l == r) return;
        int m = (l + r) >> 1;
        if(p <= b[m])  update(ls[o], l, m, ls[last], p);
        else update(rs[o], m + 1, r, rs[last], p);
    }
    
    int query(int ss, int tt, int l, int r, int k){
        if(l == r) return l;
        int m = (l + r) >> 1;
        int cnt = sum[ls[tt]] - sum[ls[ss]];
        if(k <= cnt) return query(ls[ss], ls[tt], l, m, k);
        else return query(rs[ss], rs[tt], m + 1, r, k - cnt);
    }
    
    void work(){
        scanf("%d%d%d", &ql, &qr, &x);
        int ans = query(rt[ql - 1], rt[qr], 1, sz, x);
        printf("%d
    ", b[ans]);
    }
    
    int main(){freopen("t.txt","r",stdin);
       T=1; 
        while(T--){
            scanf("%d%d", &n, &q);
            for(int i = 1; i <= n; i ++) scanf("%d", a + i), b[i] = a[i];
            sort(b + 1, b + n + 1);
            sz = unique(b + 1, b + n + 1) - (b + 1);
            tot = 0;
            Build(rt[0],1, sz);
            //for(int i = 0; i <= 4 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d
    ", i, rt[i], ls[i], rs[i], sum[i]);
            //for(int i = 1; i <= n; i ++)a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b;
            for(int i = 1; i <= n; i ++)update(rt[i], 1, sz, rt[i - 1], a[i]);
           // for(int i = 0; i <= 5 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d
    ", i, rt[i], ls[i], rs[i], sum[i]);
            while(q --)work();
        }
        return 0;
    }
    

      

  • 相关阅读:
    Idea快捷键
    基于LinkedHashMap实现一个基于LRU算法的缓存设计
    SpringBoot整合Mybatis
    MyBatis Generator自动生成代码
    java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
    Dubbo的异步调用
    Jackson的ObjectMapper的简单用法及语法
    mysql实例代码
    6. Z 字形变换
    13. 罗马数字转整数
  • 原文地址:https://www.cnblogs.com/heisenberg-/p/6869410.html
Copyright © 2020-2023  润新知