poj2104-求区间第k大数(不修改)主席树/可持续化线段树

Posted Konjak谷弱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj2104-求区间第k大数(不修改)主席树/可持续化线段树相关的知识,希望对你有一定的参考价值。

第一道主席树~然而是道比较水的。。。因为它不用修改。。。

转载一个让我看懂的主席树的讲解吧:http://blog.csdn.net/regina8023/article/details/41910615 (未授权,侵权删)

---------------------------------------------------------------------------------------------------------------------

 

那么如果要询问i-j之间数字出现的次数怎么办呢?

因为每一棵线段树的区间都是相同的,所以要求l-r之间的数字的出现次数只要用前r位出现的次数减去前l-1位出现的次数,就是ans

 

但是如果有修改操作怎么办?

如果沿用上面的做法,那么修改操作是O(nlogn)的,查询是O(1)的,修改要花好长时间。。。

前缀和联想到了树状数组,那么将前缀和用树状数组维护的话修改是O(logn*logn),查询时O(logn),查询的时间虽然变长,但是修改的时间缩短许多!!

 

注意:

函数式线段树的数组要开大一点!!

---------------------------------------------------------------------------------------------------------------------

 

这题就是模版题啦,先离散,求区间第k大的时候lx=root[l-1],rx=root[r],两边同时走不断作差,看看左孩子的数量,如果k更大就减掉左孩子的到有右孩子中找。

代码:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<iostream>
  6 #include<algorithm>
  7 using namespace std;
  8 
  9 const int N=100010,INF=(int)1e9+100;
 10 struct trnode{
 11     int lc,rc,cnt;
 12 }t[30*N];
 13 struct node{
 14     int d,id;
 15 }p[N];
 16 int n,m,tl,mx;
 17 int a[N],num[N],root[N];
 18 
 19 bool cmp(node x,node y){return x.d<y.d;}
 20 
 21 int bt(int l,int r)
 22 {
 23     int x=++tl;
 24     // a[x].l=l;a[x].r=r;
 25     t[x].lc=t[x].rc=0;
 26     t[x].cnt=0;
 27     if(l<r)
 28     {
 29         int mid=(l+r)/2;
 30         t[x].lc=bt(l,mid);
 31         t[x].rc=bt(mid+1,r);
 32     }
 33     return x;
 34 }
 35 
 36 int add(int rt,int x)
 37 {
 38     int now=++tl,tmp=now;
 39     t[now].cnt=t[rt].cnt+1;
 40     int l=1,r=mx,mid;
 41     while(l<r)
 42     {
 43         mid=(l+r)/2;
 44         if(x<=mid)
 45         {
 46             t[now].lc=++tl;
 47             t[now].rc=t[rt].rc;
 48             rt=t[rt].lc;
 49             now=tl;
 50             r=mid;
 51         }
 52         else
 53         {
 54             t[now].lc=t[rt].lc;
 55             t[now].rc=++tl;
 56             rt=t[rt].rc;
 57             now=tl;
 58             l=mid+1;
 59         }
 60         t[now].cnt=t[rt].cnt+1;
 61     }
 62     return tmp;
 63 }
 64 
 65 int query(int lx,int rx,int k)
 66 {
 67     int l=1,r=mx,mid;
 68     while(l<r)
 69     {
 70         mid=(l+r)/2;
 71         if(t[t[rx].lc].cnt-t[t[lx].lc].cnt>=k)
 72         {
 73             r=mid;
 74             lx=t[lx].lc;
 75             rx=t[rx].lc;
 76         }
 77         else
 78         {
 79             l=mid+1;
 80             k-=t[t[rx].lc].cnt-t[t[lx].lc].cnt;
 81             lx=t[lx].rc;
 82             rx=t[rx].rc;
 83         }
 84     }
 85     return l;
 86 }
 87 
 88 void output(int x)
 89 {
 90     printf("x = %d  lc = %d  rc = %d  cnt = %d\\n",x,t[x].lc,t[x].rc,t[x].cnt);
 91     if(t[x].lc) output(t[x].lc);
 92     if(t[x].rc) output(t[x].rc);
 93 }
 94 
 95 int main()
 96 {
 97     freopen("a.in","r",stdin);
 98     while(scanf("%d%d",&n,&m)!=EOF)
 99     {
100         tl=0;mx=0;
101         for(int i=1;i<=n;i++)
102         {
103             scanf("%d",&a[i]);
104             p[i].d=a[i];p[i].id=i;
105         }
106         sort(p+1,p+1+n,cmp);
107         p[0].d=INF;
108         for(int i=1;i<=n;i++)
109         {
110             if(p[i].d!=p[i-1].d) mx++,num[mx]=p[i].d;
111             a[p[i].id]=mx;
112         }
113         root[0]=bt(1,mx);
114         for(int i=1;i<=n;i++)
115             root[i]=add(root[i-1],a[i]);
116         // output(root[5]);
117         for(int i=1;i<=m;i++)
118         {
119             int l,r,k;
120             scanf("%d%d%d",&l,&r,&k);
121             printf("%d\\n",num[query(root[l-1],root[r],k)]);
122         }
123     }
124     return 0;
125 }

 

以上是关于poj2104-求区间第k大数(不修改)主席树/可持续化线段树的主要内容,如果未能解决你的问题,请参考以下文章

主席树的各类模板(区间第k大数动,静,区间不同数的个数,区间<=k的个数)

poj2104求区间第k小,静态主席树入门模板

POJ2104 K-th Number 不带修改的主席树 线段树

[poj 2104]主席树+静态区间第k大

主席树——求静态区间第k大

bzoj2104 K-th Number