P3834-【模板】可持久化线段树 1(主席树)
正題
評(píng)測(cè)記錄:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3834
題意
給定一個(gè)長(zhǎng)度為n的序列,有m個(gè)詢問(wèn),求一個(gè)區(qū)間內(nèi)的第k小的樹。
解題思路
我們先思考用線段樹快速詢問(wèn)第k小的樹 
 我們可以用權(quán)值線段樹來(lái)處理第k小的樹,先將數(shù)組離散化(排序加去重),然后下標(biāo)[l...r][l...r]表示在離散化序列內(nèi)l~rl~r中的數(shù)出現(xiàn)過(guò)多少次,然后因?yàn)橐呀?jīng)離散化了所以我們可以直接根據(jù)兩個(gè)子節(jié)點(diǎn)返回的值來(lái)進(jìn)行向下尋找。 
 我們開始考慮區(qū)間問(wèn)題。區(qū)間問(wèn)題就是只算上這個(gè)區(qū)間內(nèi)的數(shù)的線段樹,我們可以使用前綴和。建立n個(gè)線段樹表示1~x1~x這個(gè)區(qū)間,然后每次查詢就是用第rr棵線段樹減去·第l?1l?1棵線段樹就好了。 
 但是這么一算其實(shí)時(shí)間復(fù)雜度和空間復(fù)雜度還不如暴力那就直接暴力吧。這么一想我們會(huì)發(fā)現(xiàn)其實(shí)每次修改都只修改一條鏈,那么我們?yōu)槭裁床幻看沃辉黾右粭l鏈呢? 
 那么我們開始今天的正題:可持久化線段樹(主席樹)。 
 直接給出結(jié)構(gòu) 
  
 (上面的數(shù)是區(qū)間范圍,下面的是值。綠色為新增加的點(diǎn)) 
 我們每次只修改一條鏈,然后沒(méi)有修改的那一部分和原來(lái)的節(jié)點(diǎn)相連,每次詢問(wèn)不同版本時(shí)只需要從不同版本的根節(jié)點(diǎn)開始訪問(wèn)就可以了。而這樣我們就需要?jiǎng)討B(tài)開點(diǎn)了。
代碼
#include<cstdio> #include<algorithm> #define MN 200010 using namespace std; struct tnode{int w,l,r,ls,rs; }t[MN<<5]; int n,m,x,y,k,a[MN],b[MN],root[MN],num,q,w; int build(int l,int r)//建立一棵空樹 {int k=++num;t[k].l=l,t[k].r=r;if (l==r) return k;int mid=(l+r)>>1;t[k].ls=build(l,mid);t[k].rs=build(mid+1,r);//左右分區(qū)return k;//返回編號(hào) } int addt(int k,int z)//建立一條新鏈 {int nb=++num;t[nb]=t[k];t[nb].w++;//動(dòng)態(tài)開點(diǎn)if (t[k].l==z&&t[k].r==z) return nb;//到達(dá)節(jié)點(diǎn)int mid=(t[k].l+t[k].r)>>1;if (z<=mid) t[nb].ls=addt(t[k].ls,z);else t[nb].rs=addt(t[k].rs,z);//建立下一個(gè)節(jié)點(diǎn)return nb; } int query(int k1,int k2,int k)//前綴和詢問(wèn)區(qū)間 {if (t[k1].l==t[k1].r) return t[k1].l;w=t[t[k2].ls].w-t[t[k1].ls].w;//計(jì)算左子樹在這個(gè)區(qū)間內(nèi)的數(shù)字?jǐn)?shù)量if (k<=w) return query(t[k1].ls,t[k2].ls,k);else return query(t[k1].rs,t[k2].rs,k-w);//向下詢問(wèn) } int main() {scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];sort(b+1,b+1+n);//排序(離散化1)int q=unique(b+1,b+1+n)-b-1;//去重(離散化)root[0]=build(1,q);//建立空樹for (int i=1;i<=n;i++){int te=lower_bound(b+1,b+1+q,a[i])-b;//二分尋找原來(lái)的位置root[i]=addt(root[i-1],te);//建立一條鏈}for (int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&k);printf("%d\n",b[query(root[x-1],root[y],k)]);//詢問(wèn)區(qū)間} }總結(jié)
以上是生活随笔為你收集整理的P3834-【模板】可持久化线段树 1(主席树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: cad乘号怎么打 3个步骤输入乘号
- 下一篇: 张学友简介 张学友个人简介
