• 主席树(区间第k小)


    //主席树 权值线段树+可持久化 
    //权值线段树:在此处指各个数字在某个区间内出现的次数 
    //那么第一棵权值线段树会记录[1,1]的数字出现次数 
    //第n棵权值线段树会记录[1,n]的数字出现次数 
    //例:数列为110001
    //第一棵权值线段树记录为tree1[0]=0 tree1[1]=1
    //第二棵权值线段树记录为tree2[0]=0 tree2[1]=2
    //第六棵权值线段树记录为tree6[0]=3 tree6[1]=3
    //那么要求区间为[3,5]的数字出现次数可拿第五棵权值线段树减去第(三减一)棵权值线段树 
    //此处运用前缀和思想 
    //将多棵权值线段树的公共点合为一个点可减少空间复杂度 
    //求区间第k小即可求出区间数字出现次数后递归操作 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,cnt,b[200001],root[200001];//b[]离散化后的值 root[]根的编号 
    struct uio{
        int num,id;
    }a[200001];//离散化辅助结构体 
    struct rty{
        int ls,rs,siz;//左儿子,右儿子,区间元素个数 
    }tree[5000001];//权值线段树 递增使得左儿子表示的值小于右儿子表示的值 
    bool cmp(uio x,uio y)
    {
        return x.num<y.num;
    }
    void update(int l,int r,int k,int &now)//新建节点的左右儿子,新建节点值,当前节点 
    {
        tree[++cnt]=tree[now];
        now=cnt;
        tree[now].siz++;
        if(l==r)
            return;
        int mid=(l+r)/2;
        if(k<=mid)//新建节点在左子树 
            update(l,mid,k,tree[now].ls);
        else update(mid+1,r,k,tree[now].rs);//新建节点在右子树 
    }
    int query(int l,int r,int x,int y,int k)//左右儿子,两棵权值线段树编号,第k大  
    {
        if(l==r) 
            return l;
        int dif=tree[tree[y].ls].siz-tree[tree[x].ls].siz;//见本代码第9行 
        int mid=(l+r)/2;
        if(k<=dif)//左儿子的出现次数已大于等于k 说明k在左子树 
            return query(l,mid,tree[x].ls,tree[y].ls,k);
        else return query(mid+1,r,tree[x].rs,tree[y].rs,k-dif);//k在右子树
        //k不在左子树 但是左子树代表的数的出现次数包含在k中 因此要减去(同平衡树) 
    }
    void do_something()
    {
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            printf("%d
    ",a[query(1,n,root[u-1],root[v],w)].num);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].num);
            a[i].id=i;
        }
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;i++)
            b[a[i].id]=i;//离散化 
        for(int i=1;i<=n;i++)
        {
            root[i]=root[i-1];//暂时将新的根节点赋为原根节点的编号 
            update(1,n,b[i],root[i]);//新建一棵权值线段树 
        }
        do_something();
         return 0;
    }
  • 相关阅读:
    如何更好的学习编译原理?
    组合数据类型练习
    简化版c语言文法
    Python基础练习
    Linux 命令
    实验一:词法分析实验报告
    20160930 词法分析程序
    大数据概述
    WP7应用开发笔记(7) 配置和存储
    欧拉计划 第九题
  • 原文地址:https://www.cnblogs.com/water-radish/p/9280887.html
Copyright © 2020-2023  润新知