日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

主席树之初见

發(fā)布時(shí)間:2024/9/5 编程问答 87 豆豆
生活随笔 收集整理的這篇文章主要介紹了 主席树之初见 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?何為主席樹

?          圖1?

主席樹的構(gòu)造如圖,以前序遍歷的方式編號(hào),葉子表示1到n

因?yàn)槿~子是1到n,就有了左子樹總是小于右子樹的性質(zhì)

除葉子外的節(jié)點(diǎn)記錄的是區(qū)間sum代表這個(gè)節(jié)點(diǎn)的葉子有多少個(gè)數(shù)

如圖 區(qū)間[2,2]有1個(gè)數(shù),區(qū)間[3,3]有1個(gè)數(shù)

所以區(qū)間[1,2]有1個(gè)數(shù),區(qū)間[3,4]有2個(gè)數(shù),區(qū)間[1,4]有3個(gè)數(shù)

?性質(zhì)

  • 左子樹總是小于右子樹

因?yàn)閺淖蟮接胰~子是1到n

  ? ?設(shè)樹的sum=x+y,左子樹sum=x,右子樹sum=y

所以在找第k小的時(shí)候,由于左子樹小于右子樹,所以前x小肯定在左子樹上,第x+1到第x+y小在右子樹上

  ? ?當(dāng)k<=x時(shí),就在左子樹上找,當(dāng)k>x時(shí)在右子樹上找,

  ? ?在右子樹上找時(shí),由于右子樹第一個(gè)是第x+1小,所以找第k小也就是找右子樹的第k-x小

  • 區(qū)間可減性

 由于sum記錄的是個(gè)數(shù),假設(shè)現(xiàn)在是第L棵樹,經(jīng)過n次操作變成第R棵樹

 由于每次操作都是修改一次,所以R的左子樹sum-L的左子樹sum=從L到R的左子樹的操作數(shù)

 所以求[L,R]區(qū)間的第k小,可以利用R的左子樹sum-L的左子樹sum即為[L,R]內(nèi)左子樹上的數(shù)的個(gè)數(shù)

?                    圖2

?

如圖 經(jīng)過(L,R]區(qū)間的增加的左子樹的個(gè)數(shù)為2,也就是R的左子樹sum-L的左子樹的sum

?

?例題

洛谷3919

這個(gè)題是可持久化的題,并非完全的主席樹

這個(gè)題的葉子的值不是1到n,而是根據(jù)輸入而定

也沒有用到上述的性質(zhì)

利用這個(gè)題來學(xué)習(xí)可持久化,為主席樹做鋪墊

1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+5; 4 struct Node 5 { 6 int l,r; 7 int num;///num為值,這里與主席樹不同 8 Node(){num=0;} 9 }seg[maxn*20]; 10 int root[maxn],a[maxn]; 11 int cnt=0; 12 void build(int l,int r,int &rt)///以前序遍歷的方式建樹 13 { 14 rt=++cnt;///rt為編號(hào) 15 if(l==r) 16 { 17 seg[rt].num=a[l]; 18 return ; 19 } 20 int mid=(r+l)>>1; 21 build(l,mid,seg[rt].l); 22 build(mid+1,r,seg[rt].r); 23 } 24 25 void update(int l,int r,int &rt,int pos,int y) 26 { 27 seg[++cnt]=seg[rt];///復(fù)制前一棵樹 28 rt=cnt;///處理所新建的這棵樹 29 if(l==r) 30 { 31 seg[rt].num=y; 32 return ; 33 } 34 int mid=(r+l)>>1; 35 if(pos<=mid) update(l,mid,seg[rt].l,pos,y); 36 else update(mid+1,r,seg[rt].r,pos,y); 37 } 38 39 int query(int l,int r,int rt,int pos) 40 { 41 if(l==r) return seg[rt].num; 42 int mid=(r+l)>>1; 43 if(pos<=mid) return query(l,mid,seg[rt].l,pos); 44 else return query(mid+1,r,seg[rt].r,pos); 45 } 46 47 int main() 48 { 49 ios::sync_with_stdio(0); 50 int n,m; 51 cin>>n>>m; 52 for(int i=1;i<=n;i++) 53 cin>>a[i]; 54 build(1,n,root[0]); 55 int pre,op,x,y; 56 for(int i=1;i<=m;i++) 57 { 58 cin>>pre>>op>>x; 59 if(op==1) 60 { 61 cin>>y; 62 root[i]=root[pre]; 63 update(1,n,root[i],x,y); 64 } 65 else 66 { 67 cout<<query(1,n,root[pre],x)<<endl; 68 root[i]=root[pre]; 69 } 70 } 71 } View Code

poj2104

主席樹的模板題,用到了上述的性質(zhì)

由于剛開始沒有值,我就沒有建樹

其實(shí)做模板的話可以建一個(gè)空樹

由于葉子是從1到n的,所以對(duì)于給出的數(shù)一般要離散化一下,使得對(duì)應(yīng)1到n

1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 #define ll long long 5 const int maxn=1e5+5; 6 int cnt,root[maxn];///每棵樹的根 7 int index[maxn];///按值大小排序后的序號(hào) 8 struct node 9 { 10 int l,r; 11 int sum; 12 node(){sum=0;} 13 }seg[maxn*40]; 14 struct value 15 { 16 int v; 17 int id; 18 }V[maxn]; 19 bool cmp(value a,value b) 20 { 21 return a.v<b.v; 22 } 23 24 void init() 25 { 26 cnt=0; 27 seg[0].l=0,seg[0].r=0; 28 root[0]=0; 29 } 30 31 void build(int l,int r,int &rt)///以前序遍歷的方式建樹 32 { 33 rt=++cnt;///rt為編號(hào) 34 if(l==r) 35 return ; 36 int mid=(r+l)>>1; 37 build(l,mid,seg[rt].l); 38 build(mid+1,r,seg[rt].r); 39 } 40 41 void update(int l,int r,int &rt,int pos) 42 { 43 seg[++cnt]=seg[rt];///復(fù)制前一棵樹 44 rt=cnt;///因?yàn)閷?duì)新樹進(jìn)行操作,是rt為新樹 45 seg[rt].sum++;///進(jìn)行操作了,個(gè)數(shù)+1 46 47 if(l==r) return ; 48 int mid=(l+r)>>1; 49 if(mid>=pos) 50 update(l,mid,seg[rt].l,pos); 51 else 52 update(mid+1,r,seg[rt].r,pos); 53 } 54 55 int query(int L,int R,int l,int r,int k) 56 { 57 int d=seg[seg[R].l].sum-seg[seg[L].l].sum;///利用區(qū)間可減性,左子樹上一共有d個(gè)數(shù) 58 if(l==r) return l; 59 int mid=(l+r)>>1; 60 if(d>=k)///利用 左子樹<右子樹 前d個(gè)在左子樹上,其他的在右子樹上 61 return query(seg[L].l,seg[R].l,l,mid,k); 62 else///右子樹第一個(gè)是第d+1個(gè) 找第k個(gè)也就是找右子樹的第k-d個(gè) 63 return query(seg[L].r,seg[R].r,mid+1,r,k-d); 64 65 } 66 67 int main() 68 { 69 int n,m; 70 cin>>n>>m; 71 for(int i=1;i<=n;i++) 72 { 73 cin>>V[i].v; 74 V[i].id=i; 75 } 76 ///離散化 77 sort(V+1,V+1+n,cmp); 78 for(int i=1;i<=n;i++) 79 index[V[i].id]=i; 80 init(); 81 // build(1,n,root[0]); 82 for(int i=1;i<=n;i++) 83 { 84 root[i]=root[i-1];///復(fù)制前一個(gè)樹 85 update(1,n,root[i],index[i]); 86 } 87 int L,R,k; 88 for(int i=1;i<=m;i++) 89 { 90 cin>>L>>R>>k; 91 cout<<V[query(root[L-1],root[R],1,n,k)].v<<endl; 92 } 93 } View Code

?hdu4417

查詢$[L,R]$區(qū)間里有多少個(gè)數(shù)不大于k

只需要修改一下query函數(shù)

把k也離散化一下,利用k是第x小,答案就是

第R棵樹的[0,k]數(shù)量-第(L-1)棵樹的[0,k]數(shù)量

因?yàn)檫@個(gè)題是從$L,R\epsilon[0,n-1]$而不是從$1$開始,需要注意坑點(diǎn)

因?yàn)槭菑?1$到$n$的樹,需要記得$L++,R++$

因?yàn)殡x散化后會(huì)從$k\epsilon[0,n]$,所有需要從0開始建樹更新查詢,防止從$1$開始找不到$0$位置

1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+5; 4 struct node 5 { 6 int l,r; 7 int sum; 8 node(){sum=0;} 9 }seg[maxn*20]; 10 int root[maxn]; 11 int cnt=0; 12 int a[maxn],b[maxn]; 13 14 void Init() 15 { 16 cnt=0; 17 root[0]=0; 18 seg[0].l=0,seg[0].r=0; 19 } 20 21 void build(int l,int r,int &rt) 22 { 23 rt=++cnt; 24 if(l==r) return ; 25 int mid=(l+r)>>1; 26 build(l,mid,seg[rt].l); 27 build(mid+1,r,seg[rt].r); 28 } 29 30 void update(int l,int r,int &rt,int pos) 31 { 32 seg[++cnt]=seg[rt]; 33 rt=cnt; 34 seg[rt].sum++; 35 if(l==r) 36 return ; 37 int mid=(r+l)>>1; 38 if(pos<=mid) return update(l,mid,seg[rt].l,pos); 39 else return update(mid+1,r,seg[rt].r,pos); 40 } 41 42 43 ///query第R棵樹的[0,k]數(shù)量-第(L-1)棵樹的[0,k]數(shù)量 44 ///詢問方法1 45 int query1(int L,int R,int l,int r,int k) 46 { 47 if(l==r)///[0,k]中某個(gè)位置的增量 48 return seg[R].sum-seg[L].sum; 49 int mid=(l+r)>>1; 50 if(mid>=k) 51 return query(seg[L].l,seg[R].l,l,mid,k); 52 else///遞歸右子樹,需要加上左子樹的增量 53 { 54 int d=seg[seg[R].l].sum-seg[seg[L].l].sum; 55 return d+query(seg[L].r,seg[R].r,mid+1,r,k); 56 } 57 } 58 ///詢問方法2 59 int query2(int rt,int l,int r,int k) 60 { 61 if(l==r) 62 return seg[rt].sum; 63 int mid=(r+l)>>1; 64 if(mid>=k) 65 return query(seg[rt].l,l,mid,k); 66 else 67 return seg[seg[rt].l].sum+query(seg[rt].r,mid+1,r,k); 68 } 69 70 71 int main() 72 { 73 // freopen("C:\\Users\\14685\\Desktop\\C++workspace\\in&out\\contest","r",stdin); 74 int t; 75 cin>>t; 76 for(int T=1;T<=t;T++) 77 { 78 int n,m; 79 cin>>n>>m; 80 for(int i=1;i<=n;i++) 81 cin>>a[i],b[i]=a[i]; 82 83 sort(b+1,b+1+n); 84 int len=unique(b+1,b+n+1)-(b+1); 85 Init(); 86 build(0,len,root[0]); 87 for(int i=1;i<=n;i++) 88 { 89 root[i]=root[i-1]; 90 update(0,len,root[i],upper_bound(b+1,b+1+len,a[i])-b-1); 91 } 92 93 printf("Case %d:\n",T); 94 95 while(m--) 96 { 97 int L,R,k; 98 cin>>L>>R>>k; 99 L++,R++; 100 k=upper_bound(b+1,b+1+len,k)-b-1; 101 // cout<<query1(root[L-1],root[R],1,len,k)<<endl; 102 cout<<query2(root[R],0,len,k)-query2(root[L-1],0,len,k)<<endl; 103 } 104 } 105 } View Code

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/MMMinoz/p/11440109.html

總結(jié)

以上是生活随笔為你收集整理的主席树之初见的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。