zoukankan      html  css  js  c++  java
  • 【POJ2104】K-th Number-主席树(可持久化线段树)+离散化

    测试地址:K-th Number

    题目大意:给定一个长度为N的数列,要求处理M个询问,每个询问三个参数i,j,k,表示询问区间[i,j]内第k小的数。

    做法:可持久化线段树的单点修改,网上有很多这里就不讲了。这里再简述一下这一题如何转化成可持久化线段树。首先将数列里所有数离散化成1,2,...,N,可持久化线段树的每一个节点维护一个权值区间,再维护一个cnt表示这个区间内的数出现过了多少次,一开始所有节点的cnt都初始化成0,建立一棵初始(第0棵)线段树。然后我们逐个加入原数列中的元素A[i](当然是离散化后的),这就等价于对线段树进行单点修改并产生一个新的版本,具体来说就是复制所有包含A[i]的权值区间的节点,然后在新复制的节点上的cnt增加1。这时候我们就可以知道,第i棵线段树上表示[l,r]这个权值区间的节点中cnt的意义就是:在原数列的区间[1,i]中,权值在权值区间[l,r]中的元素个数。

    加入所有元素之后,我们就可以用二分查找来询问区间[i,j]的第k小值。要进行二分查找,首先要算出在原数列的区间[i,j]中权值在某一个权值区间中的元素个数,由前面的cnt的意义可知,N棵线段树其实是一种前缀和,因此把第j棵线段树与第i-1棵线段树的对应节点cnt值相减,所得到的线段树就可以表示出在原数列的区间[i,j]中权值在某一个权值区间中的元素个数。这棵线段树不用存起来,可以在二分查找时用相对应的节点直接相减得到。知道这一点之后,就可以在这棵线段树上对权值区间[1,N]进行二分查找,如果权值区间[1,N/2]中元素的出现次数不超过k,说明第k小的值在区间[1,N/2]中,从而继续向左子节点查找,否则说明第k小的值在区间(N/2,N]中,向右子节点查找。最后注意输出的值是离散化前的值即可。

    可持久化线段树内存是硬伤......一定要开足数组!

    上面讲了这么多,实际操作起来不难,代码连100行都不到,以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,a[100010],tot=0,rt[100010];
    struct segnode
    {
      int lc,rc;
      int cnt;
    }seg[4000010];
    struct forsort
    {
      int val,pos;
    }fs[100010];
    
    bool cmp(forsort a,forsort b)
    {
      return a.val<b.val;
    }
    
    void buildtree(int no,int l,int r)
    {
      seg[no].cnt=0;
      tot=max(tot,no);
      if (l==r)
      {
        seg[no].lc=seg[no].rc=0;
    	return;
      }
      seg[no].lc=no<<1,seg[no].rc=no<<1|1;
      int mid=(l+r)>>1;
      buildtree(no<<1,l,mid);
      buildtree(no<<1|1,mid+1,r);
    }
    
    void insert(int &no,int last,int l,int r,int x)
    {
      no=++tot;
      seg[no].cnt=seg[last].cnt;
      seg[no].lc=seg[last].lc;
      seg[no].rc=seg[last].rc;
      if (l==r)
      {
        seg[no].cnt++;
    	return;
      }
      int mid=(l+r)>>1;
      if (x<=mid) insert(seg[no].lc,seg[last].lc,l,mid,x);
      else insert(seg[no].rc,seg[last].rc,mid+1,r,x);
      seg[no].cnt=seg[seg[no].lc].cnt+seg[seg[no].rc].cnt;
    }
    
    int query(int fnt,int lst,int l,int r,int k)
    {
      if (l==r) return l;
      int mid=(l+r)>>1,s=seg[seg[lst].lc].cnt-seg[seg[fnt].lc].cnt;
      if (s>=k) return query(seg[fnt].lc,seg[lst].lc,l,mid,k);
      else return query(seg[fnt].rc,seg[lst].rc,mid+1,r,k-s);
    }
    
    int main()
    {
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)
      {
        scanf("%d",&fs[i].val);
    	fs[i].pos=i;
      }
      
      sort(fs+1,fs+n+1,cmp);
      for(int i=1;i<=n;i++)
        a[fs[i].pos]=i;
      buildtree(1,1,n);
      rt[0]=1;
      for(int i=1;i<=n;i++)
        insert(rt[i],rt[i-1],1,n,a[i]);
      for(int i=1,s,t,k;i<=m;i++)
      {
        scanf("%d%d%d",&s,&t,&k);
    	printf("%d
    ",fs[query(rt[s-1],rt[t],1,n,k)].val);
      }
      
      return 0;
    }
    


  • 相关阅读:
    UI、JS框架----Bootstrap、Metro
    重构—改善既有代码的设计4——构筑测试体系
    重构—改善既有代码的设计3——代码的坏味道
    正则匹配-URL-域名
    pgAdmin的数据恢复
    重构—改善既有代码的设计2
    重构—改善既有代码的设计1
    Levenshtein Distance,判断字符串的相似性
    最大匹配字符串LCS,The Longest Common Substring
    email
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793808.html
Copyright © 2011-2022 走看看