jsp中的常用指令碼
可持久化資料結構可以儲存資料結構的所有歷史版本,並通過重用資料減少時間和空間消耗。即對每一次操作之後,僅僅對已修改部分建立副本,對其他部分重用,由此可以高效記錄資料結構的所有歷史結構狀態
下面講解可持久化線段樹
1權值線段樹
和線段樹類似,只是儲存的內容不同,即在在每一次操作後在每一個區間儲存該區間存放的數的個數
注:根節點區間表示最大與最小
例:對陣列\(a[]={(3,1,4,2,3,5,3,4)}\)
一個一個存入線段樹如下(全部存完):
存了5個數如下:
可以看到第8棵樹減去第5棵樹可以得到\([6,8]\)的序列樹,即\(5,3,4\)
可以看出其實就是字首和思想
2可持續化線段樹
同樣利用權值線段樹進行儲存
第一種狀態\(rt[0]\)為樹根--全部賦值為0
將元素逐個插入線段樹中得到以\(rt[1]、rt[2]、...rt[n]\)為根節點的新樹,並且與之前相連線,即向右邊擴充套件
當要查詢插入第3個元素後的線段樹,只需要從第三棵樹\(rt[3]\)開始查詢即可
小操作:陣列離散化
輸入序列\(a\)之後複製一份到\(b\),對\(b\)排序以後用一個新的函式
sort(b+1,b+n+1);
int tot=unique(b+1,b+n+1)-b-1;
就可以得到去重後序列元素的個數
同時
lower_bound(b+1,b+tot+1,a[i])-b;//在b[]中查詢第一個大於或等於a[i]的元素的下標
即把\(a[i]\)的下標插入到線段樹中(對\(a[i]\)的每一個數,找到其在\(b[]\)中的位置)
那麼就可以開始建立可持續化線段樹了
for(int i=1,i<=n;i++)
update(rt[i],rt[i-1],1,tot,lower_bound(b+1,b+tot+1,a[i])-b);
插入操作
int cnt=0; void update(int &i,int j,int l,int r,int k){//插入元素k i=++cnt; t[i]=t[j];//複製以前的版本 t[i].num++;//元素個數++ if(l==r) return ; if(k<=mid) update(lc,Lc,l,mid,k);//lc、rc表示t[i]的左右節點 else update(rc,Rc,mid+1,r,k);//Lc、Rc表示t[j]的左右子節點 }
例題區間第k小數
這道題將n個數先複製一遍,在排序去重,將\(a[i]\)的每一個數的在\(b[]\)中的下標插入主席樹中,當輸入\(l,r,k\)時,開始查詢
while(m--){
scanf("%d%d%d",&l,&r,&k);
cout<<b[query(rt[l-1],rt[r],1,tot,k)]<<endl;
}
從樹根\(rt[r]\)和\(rt[l-1]\)開始,若\(l==r\),則返回\(l\);再將當前兩個節點的左子樹權值相減得到\(s\)表示a序列區間\([l,r]\)插入了\(s\)個數,如果\(k<=s\),表示k在小範圍之間,則在左子樹中找,反之,說明k在大範圍,則在右子樹中找第k-s小的數(即在主席樹中找\([l,r]\)的第k小數在\(b[]\)中的下標)(左子樹的範圍總比右子樹小)
查詢程式碼:
int query(int i,int j,int l,int r,int k){//注意返回的是下標,儲存的也是下標
if(l==r) return l;
int s=t[Lc].num-t[lc].num;
if(k<=s) return query(lc,Lc,l,mid,k);
else return query(rc,Rc,mid+1,r,k-s);
}
總程式碼:
#include<bits/stdc++.h>
#define lc t[i].ch[0]
#define rc t[i].ch[1]
#define Lc t[j].ch[0]
#define Rc t[j].ch[1]
#define mid (l+r>>1)
#define N 200005
using namespace std;
int a[N],b[N];
int cnt,rt[N];
struct tree{
int num,ch[2];
}t[N*20];
void update(int &i,int j,int l,int r,int k){
i=++cnt;
t[i]=t[j];
t[i].num++;
if(l==r) return ;
if(k<=mid) update(lc,Lc,l,mid,k);//lc、rc表示t[i]的左右節點
else update(rc,Rc,mid+1,r,k);//Lc、Rc表示t[j]的左右子節點
}
int query(int i,int j,int l,int r,int k){
if(l==r) return l;
int s=t[Lc].num-t[lc].num;
if(k<=s) return query(lc,Lc,l,mid,k);
else return query(rc,Rc,mid+1,r,k-s);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
int tot=unique(b+1,b+n+1)-b-1;
cnt=0,rt[0]=0;
for(int i=1;i<=n;i++)
update(rt[i],rt[i-1],1,tot,lower_bound(b+1,b+tot+1,a[i])-b);
int l,r,k;
while(m--){
scanf("%d%d%d",&l,&r,&k);
cout<<b[query(rt[l-1],rt[r],1,tot,k)]<<endl;
}
return 0;
}
感謝觀看
PS.