zoukankan      html  css  js  c++  java
  • 【算法】 口胡主席树

      “可持久化”几个字限制我的想象力,导致我长久没有去接触这种强大的数据结构,主席树的思想如下:

      在线段树中,不难发现,每次修改与查询的时间复杂度都是o(logn)的,这是因为每次操作所要调用与修改的节点也是logn个。

      在一些题目中,题目要求我们查询线段树(尤其是值域线段树)的历史版本,这就可以用到主席树这一数据结构,每次修改都只新增logn个新节点,并记录下来每次修改后根的位置,每次查询都从某版本的根开始,就可以方便快捷的操作,同时把时间复杂度卡死在o(logn)。

       题目:区间第K大

      主席树的经典题,运用一点前缀和的思想。A1~A2,A1~A2......A1~An每个区间建立起一棵值域线段树。把数组中每一个数看作插入操作,从头到尾相当于建立了n个版本的主席树。每次查询的时候,一个区间内数的个数便可以用差分求出,然后用线段树常规求第K大可以了。

      代码(粪山,LC原话):

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define maxn 100005*160
    #define lc(a1) t[a1].ch[0]
    #define rc(a1) t[a1].ch[1]
    #define sum(a1) t[a1].sum
    #define left(a1) t[a1].left
    #define right(a1) t[a1].right
    
    struct hjt_tree {
        int ch[2],sum,left,right;
    }t[maxn];
    int a[maxn],b[maxn],n,m,size,rt[maxn];
    
    void change(int,int,int);
    int query(int,int,int);
    void build(int,int,int);
    
    int main() {
        scanf("%d%d",&n,&m);
        for (int i = 1;i <= n;i++) scanf("%d",&a[i]), b[i] = a[i];
        std::sort(b + 1,b + n + 1);
        int len = std::unique(b + 1,b + n + 1) - b - 1;
        build(rt[0] = 0,1,n);
        for (int i = 1;i <= n;i++) {
            int loc = std::lower_bound(b + 1,b + len + 1,a[i]) - b;
            change(rt[i] = ++size,rt[i - 1],loc);
        }
        for (int i = 1;i <= m;i++) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            printf("%d\n",b[query(rt[x - 1],rt[y],z)]);
        }
        return 0;
    }
    
    void build(int x,int l,int r) {
        sum(x) = 0; left(x) = l; right(x) = r;
    
        if (l == r) return ;
    
        int mid = (l + r) / 2;
        build(lc(x) = ++size,l,mid);
        build(rc(x) = ++size,mid + 1,r);
    }
    void change(int x,int ori,int loc) {
        sum(x) = sum(ori) + 1; lc(x) = lc(ori); rc(x) = rc(ori);
        left(x) = left(ori); right(x) = right(ori);
    
        if (left(x) == right(x)) return ;
    
        int mid = (left(x) + right(x)) / 2;
         if (loc <= mid) 
            change(lc(x) = ++size,lc(ori),loc);
        else  
            change(rc(x) = ++size,rc(ori),loc);
    }
    int query(int x,int y,int k) {
        int know = sum(lc(y)) - sum(lc(x));
    
        if (left(x) == right(x)) return left(x);
    
        int mid = (left(x) + right(x)) / 2;
        if (k <= know)
            return query(lc(x),lc(y),k);
        else
            return query(rc(x),rc(y),k-know);
    }
  • 相关阅读:
    C++中的指针常量与常量指针
    Ubuntu16.04下安装ROS kinetic常见问题及解决方法
    关于安装ROS的资料备份
    后台模块--删除、修改用户信息
    客车网上售票系统--查询、添加用户
    客车网上售票系统--登录
    客车网上售票系统--需求分析(一)
    简单的邮件发送器(二)
    简单的邮件发送器(一)
    在CMD上用telnet远程登录发送邮件测试记录
  • 原文地址:https://www.cnblogs.com/Illusions/p/13567292.html
Copyright © 2011-2022 走看看