zoukankan      html  css  js  c++  java
  • [POJ 2104]K-th Number 主席树 可持久化线段树 入门

    题目链接 http://poj.org/problem?id=2104

    动态找区间第k大 

    在不知道主席树前想过啥。。。每给一个区间给每个区间排序。。。知道主席树后感觉好神奇。维护从小到大数的个数的前缀和,还有很多历史版本的树,通过差分来知道某个区间的前缀和。

    主席树巧妙利用了之前建过的树,节约了空间

    主席树

    1.先建一最初的树(值空)

    2.区间后移一格,加入一个结点,实际修改了logn个结点,不动的结点就赋值为之前建的结点

    3.继续后移,重复2,把整个数组每个数建一颗树。这样有一个froot[] 存每个值对应的新建的树的根节点

    板子抄的kuangbin 做了修改和注释,非递归建树,改为正着建树。

    学习链接:https://blog.csdn.net/pengwill97/article/details/80920143

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 int const maxn=1e5+10;
     7 int m,n,q,a[maxn],b[maxn],tot,froot[maxn],c[maxn*40],lson[maxn*40],rson[maxn*40];//主席树的空间开n的40倍一般够了
     8 void init_hash(int x[]){//数值离散
     9     for(int i=1;i<=n;i++)b[i]=a[i];
    10     sort(b+1,b+n+1);
    11     m=unique(b+1,b+n+1)-b-1;
    12 }
    13 int get_hash(int x){
    14     return lower_bound(b+1,b+m+1,x)-b;
    15 }
    16 
    17 int update(int root,int pos,int val){//加入一个新的值后新的树,root是前一棵树
    18     int newroot=tot++,tmp=newroot;
    19     c[newroot]=c[root]+val;//新树的结点值=原先树的值+新加的
    20     int l=1,r=m;
    21     while(l<r){
    22         int mid=(l+r)>>1;
    23         if(pos<=mid){//看修改的值位于哪颗子树上
    24             lson[newroot]=tot++;rson[newroot]=rson[root];//如果位于左子树上,则右子树的值可以利用原先的树
    25             newroot = lson[newroot];root=lson[root];
    26             r=mid;
    27         }
    28         else {
    29             rson[newroot]=tot++;lson[newroot]=lson[root];
    30             newroot=rson[newroot];root=rson[root];
    31             l=mid+1;
    32         }
    33         c[newroot]=c[root]+val;
    34     }
    35     return tmp;//新的树的根节点
    36 }
    37 int query(int lr,int rr,int k){
    38     int l=1,r=m,mid;
    39     while(l<r){
    40         mid=(l+r)>>1;        
    41         if(c[lson[rr]]-c[lson[lr]]>=k){//求l-r区间,用r树-(l-1)树 ,如果左子树有k个了,同时向左子树跳
    42             r=mid;
    43             lr=lson[lr],rr=lson[rr];
    44         }
    45         else{//右子树有j(<k)个,向右子树跳,但应该找k-j 大
    46             l=mid+1;k-=c[lson[rr]]-c[lson[lr]];
    47             lr=rson[lr],rr=rson[rr];
    48         }
    49     }
    50     return l;
    51 }
    52 int build(int l,int r){//初始建树
    53     int root=tot++;
    54     c[tot]=0;
    55     if(l!=r){
    56         int mid=(l+r)>>1;
    57         lson[root]=build(l,mid);
    58         rson[root]=build(mid+1,r);
    59     }
    60     return root;
    61 }
    62 inline int get_num(){//此题有负数!absolute value
    63     char ch;
    64     bool flag=false;
    65     int num=0;
    66     ch=getchar();
    67     while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
    68     while(ch>='0'&&ch<='9'){num=(num<<3)+(num<<1)+ch-'0';ch=getchar();}
    69     if(flag)return -1*num;
    70     return num;
    71 }
    72 int main(){
    73         scanf("%d%d",&n,&q);
    74         tot=0;
    75         for(int i=1;i<=n;i++)a[i]=get_num();
    76         
    77         init_hash(a);
    78         froot[0]= build(1,m);
    79         for(int i=1;i<=n;i++){
    80             int pos=get_hash(a[i]);
    81             froot[i]=update(froot[i-1],pos,1);
    82         }
    83         while(q--){
    84             int l,r,k;
    85             l=get_num(),r=get_num(),k=get_num();
    86             printf("%d
    ",b[query(froot[l-1],froot[r],k)]);
    87         }
    88     return 0;
    89 }
  • 相关阅读:
    Entity Framework框架 (一)
    webAPI的常用操作
    图片添加水印和生成验证码
    ASP.NET中Page_Load()与Page_Init()的区别
    session常用操作
    非递归解决组合问题
    TemplateDoesNotExist 异常
    [android]不解锁刷机
    论记忆化搜索
    flex builder 4
  • 原文地址:https://www.cnblogs.com/conver/p/11284762.html
Copyright © 2011-2022 走看看