某數據結構


前言

如果撞了國外論文,希望指出(已經撞集訓隊論文了)

1.功用

簡而言之,不帶修的線段樹/優化版的ST表。

2.時(空)間復雜度

時(空)間復雜度\(O(nlog_2log_2n)\),單次查詢\(O(log_2log_2n)\)

3.實現

類比線段樹,但我們不是每段區間分為\(2\)段,而是對於區間\([l,r]\),我們划分為大小\(k=\sqrt{r-l+1}\)的塊,共\(\lceil\frac{r-l+1}{k}\rceil\)塊。

對於每一區間,我們使用二維數組\(f[i][j]\),表示第\(i\)塊到第\(j\)塊的和(也可以是其他滿足結合律(不需要存在逆運算)的運算)。

之后就是查詢操作,我們對於整塊查詢的,在\(f\)內查詢,之后余下的塊便遞歸處理。

如圖

無標題.png

4.復雜度證明

- 前置定理

(1)若\(n^{\frac{1}{2^k}}=2\),則\(k≈log_2log_2n\)

\[n^{\frac{1}{2^k}}=2\\ \Leftrightarrow n=2^{2^k}\\ \Leftrightarrow k=log_2log_2n \]

(2)區間\([l,r]\)的二維數組大小為\(O(r-l)\)

顯然

(3)每層空間\(O(n)\)

如上圖,顯然

(4)樹深\(h=O(log_2log_2n)\)

若第\(n\)層某區間大小為\(x\),則第\(n+1\)層最大區間的大小為\(\lceil\sqrt{x}\rceil\)

如深度超過\(1\)層,則倒數第二層的單個節點區間大小為\(2或3\)

\(\therefore n=(((2^2)^2)^2\cdots (h次)),O(h)=O(\log_2\log_2n)\)

(5)樹所用空間\(O(n\log_2\log_2n)\)

由(3)(4)易得

(6)單次查詢時間復雜度\(O(\log_2\log_2n)\)

首先,對於區間\([l,r]\),它如果與樹上當前節點所代表的區間有一側重合,則至多往下遞歸一個有一側的區間,兩側重合則直接返回,無重合則遞歸兩個單側重合的區間(參考上圖)(類比線段樹的時間復雜度證明)

由於該樹深度為\(O(\log_2\log_2n)\),故時間復雜度\(O(\log_2\log_2n)\)

例題1:區間最大值
(ps:由於常數原因,下代碼只能的得92pts,如果有更優的實現可以聯系博主)

#include<bits/stdc++.h>
using namespace std;
# define ll long long
# define read read1<ll>()
# define Type template<typename T>
Type T read1(){
	T t=0;
	char k;
	bool vis=0;
	do (k=getchar())=='-'&&(vis=1);while('0'>k||k>'9');
	while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^'0'),k=getchar();
	return vis?-t:t;
}
# define fre(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
int s,m;
struct node{
	node **x;
	int **v;
	node(){}
}*root;
int Sq[100005],*sta;
int* New(int x){int *n=sta;sta+=x;return n;} 
int Max(const int &a,const int &b){return a>b?a:b;}
int build(node*& n,int l,int r){
	n=new node;
	int b=Sq[r-l+1],w=(r-l+b)/b;
	n->x=new node*[w];n->v=new int*[w];
	for(int i=0;i<w;++i)n->v[i]=New(w);
	if(l==r)return n->v[0][0]=read;
	for(int i=0,j=l;i<w;++i,j+=b)
		n->v[i][i]=build(n->x[i],j,min(r,j+b-1));
	for(int i=w-1;i>=0;--i)
		for(int j=i+1;j<w;++j)
			n->v[i][j]=Max(n->v[i][j-1],n->v[j][j]);
	return n->v[0][w-1];
}
int query(node* n,int l,int r,int tl,int tr){
	tl=Max(l,tl);tr=min(tr,r);
	if(tl>tr)return -2e9;
	if(l==r)return n->v[0][0];
	int b=Sq[r-l+1],w=(r-l+b)/b;
	int lx=(tl-l)/b,rx=(tr-l)/b,lf=lx*b+l,rf=(rx+1)*b+l-1;
	if(lx==rx)return query(n->x[lx],lf,min(rf,r),tl,tr);
	int v=-2e9;
	if(lf!=tl)v=query(n->x[lx],lf,lf+b-1,tl,tr);
	else --lx;
	if(min(rf,r)!=tr)v=Max(v,query(n->x[rx],rf-b+1,min(rf,r),tl,tr));
	else ++rx;
	if(lx+1!=rx)v=Max(v,n->v[lx+1][rx-1]);
	return v;
}
int main(){
	s=read,m=read;
	sta=new int[s*10];
	for(int i=1;i<=s;++i){
		int x=Sq[i-1];
		Sq[i]=x++;
		if(x*x<=i)Sq[i]=x;
	}
	build(root,1,s);
	for(int i=1;i<=m;++i){
		int l=read,r=read;
		printf("%d\n",query(root,1,s,l,r));
	}
	return 0;
}

2020.12.27:好像和vanEmdeBoas樹的思路很像啊

2021.7.11

可以優化至\(O(n\log\log n+1)\)

我們考慮將區間\([l,r]\)的前綴和后綴存下,然后參考\(ST\)表,每層節點的區間大小為\(2^{2^0},2^{2^1},2^{2^2}...,2^{2^{\log_2\log_2 n}}\)

二維數組還是照常

當然只有這些不行,我們需要\(n=\sum_{i=1}^m2^{2^{x_i}}\),用上面的證明,\(m=O(\log\log n)\)

\(m\)個這樣的表,同樣使用二維數組存總和

由於在每個表內的區間查詢可以參考\(ST\)表計算得到(前綴和得到邊角,二維數組得到中間),所以單次查詢時間復雜度\(O(1)\)

由於每個表時空復雜度\(O(Len\log\log Len)\),所以總空間復雜度\(O(n\log\log n+1)\)

好的,撞了2013年集訓隊論文:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM