1. 程式人生 > 其它 >jsp中的常用指令碼

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.