1. 程式人生 > >動態開點線段樹(多棵線段樹)的記憶體分配與回收

動態開點線段樹(多棵線段樹)的記憶體分配與回收

前言

線段樹,是一個很好用的能支援O(logn)區間操作的資料結構,隨著做一些稍微煩一點的題,有時候會發現有些情況要開一個數組的線段樹,更有甚者要樹套樹,而在很多情況下線段樹就不能把所有點都開滿了(否則會MLE記憶體超限),於是就出現了線段樹的動態開點寫法

基本思想

與普通的線段樹相同,動態開點線段樹只是一開始每一個節點都沒有,insert的時候,如果遇到節點是空的,那麼就宣告這個點,詢問的時候只訪問詢問的區間中非空節點,這樣一來,時間複雜度沒有問題還是O( qlogn ),空間複雜度是O(有過值節點的數量* logn


但是這麼做當遇到樹套樹的時候,空間複雜度就會炸的很慘,為O(有過值的節點的數量* log2n ),可能會到O( n2log2n
那怎麼優化呢,delete的時候如果遇到這個節點的兒子都是空的,那麼就刪掉這個點,回收這個點的記憶體,這樣空間複雜度就能優化到O(有值節點數量最多時的節點數* log2n

例題

SDOI2014旅行
https://www.luogu.org/problemnew/show/3313
題目大意
現在有n個節點的樹,每個節點上有個顏色並且有個權值,要求支援:
1.詢問樹上的一條路徑上某顏色的權值和
2.詢問樹上的一條路徑上某顏色的最大權值
3.修改某節點的權值
4.修改某節點的顏色
這一題只要樹鏈剖分,每個顏色都開一棵線段樹,支援區間求和和區間max,當然不能開完所以點,只要動態開點就好了
動態開點的打好,可以練一練回收記憶體的方法,我的方法是開一個vector儲存沒有用過的記憶體節點
這個是我的AC程式碼,我用的是指標

#include<cstdio>
#include<cstring>
#include<cctype>
namespace fast_IO
{
    const int IN_LEN=10000000,OUT_LEN=10000000;
    char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf;
    char *lastin=ibuf+IN_LEN;
    const char *lastout=ibuf+OUT_LEN-1;
    inline char getchar_()
    {
        if
(ih==lastin)lastin=ibuf+fread(ibuf,1,IN_LEN,stdin),ih=ibuf; return (*ih++); } inline void putchar_(const char x) { if(ih==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf; *oh++=x; } inline void flush(){fwrite(obuf, 1, oh - obuf, stdout);} } using namespace fast_IO; //#define getchar() getchar_() //#define putchar(x) putchar_((x)) typedef long long LL; #define rg register template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(a%b==0)return b;return gcd(b,a%b);} template <typename T> inline void read(T&x) { char cu=getchar();x=0;bool fla=0; while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();} while(isdigit(cu))x=x*10+cu-'0',cu=getchar(); if(fla)x=-x; } template <typename T> void printe(const T x) { if(x>=10)printe(x/10); putchar(x%10+'0'); } template <typename T> inline void print(const T x) { if(x<0)putchar('-'),printe(-x); else printe(x); } const int MAX=524288,MAXN=100007,MAXM=200007; int n,m,q; int head[MAXN],nxt[MAXM],tow[MAXM],tmp=1; inline void addb(const int a,const int b) { tmp++; nxt[tmp]=head[a]; head[a]=tmp; tow[tmp]=b; } int v[MAXN],fr[MAXN],fa[MAXN],size[MAXN],dep[MAXN],tim=0,son[MAXN],top[MAXN],tid[MAXN],bak[MAXN]; void dfs1(const int u,int las,int depth) { fa[u]=las; dep[u]=depth; size[u]=1; for(rg int i=head[u];~i;i=nxt[i]) { const int v=tow[i]; if(v!=las) { dfs1(v,u,depth+1); size[u]+=size[v]; if(son[u]==-1||size[son[u]]<size[v]) son[u]=v; } } } void dfs2(const int u,const int tp) { top[u]=tp; tid[u]=++tim; bak[tim]=u; if(son[u]==-1)return; dfs2(son[u],tp); for(rg int i=head[u];~i;i=nxt[i]) { int v=tow[i]; if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } } int l[MAX],r[MAX],mid[MAX]; struct node { node *lson,*rson; int irt,maxx; }Q[2000001],*root[100001]; #include<vector> std::vector<node*>bin; inline node *new_node() { node *res=bin[bin.size()-1]; bin.pop_back(); res->lson=res->rson=0; res->irt=res->maxx=0; return res; } inline void del_node(node *res) { bin.push_back(res); } void ini(const int root,const int ll,const int rr) { l[root]=ll,mid[root]=(ll+rr)>>1,r[root]=rr; if(ll==rr)return; ini(root<<1,ll,mid[root]),ini(root<<1|1,mid[root]+1,rr); } inline void update(node *ROOT) { ROOT->irt=ROOT->maxx=0; if(ROOT->lson)ROOT->irt+=ROOT->lson->irt,ROOT->maxx=max(ROOT->maxx,ROOT->lson->maxx); if(ROOT->rson)ROOT->irt+=ROOT->rson->irt,ROOT->maxx=max(ROOT->maxx,ROOT->rson->maxx); } void insert(node *ROOT,const int root,const int wan,const int ins) { if(l[root]==r[root]){ROOT->irt=ins,ROOT->maxx=ins;return;} if(wan<=mid[root]) { if(!ROOT->lson)ROOT->lson=new_node(); insert(ROOT->lson,root<<1,wan,ins); } else { if(!ROOT->rson)ROOT->rson=new_node(); insert(ROOT->rson,root<<1|1,wan,ins); } update(ROOT); } node *del(node *ROOT,const int root,const int wan) { if(l[root]==r[root]) { del_node(ROOT); return 0; } if(wan<=mid[root]) { ROOT->lson=del(ROOT->lson,root<<1,wan); if(ROOT->lson==0&&ROOT->rson==0) { del_node(ROOT); return 0; } } else { ROOT->rson=del(ROOT->rson,root<<1|1,wan); if(ROOT->lson==0&&ROOT->rson==0) { del_node(ROOT); return 0; } } update(ROOT); return ROOT; } int search_irt(node *ROOT,const int root,const int ll,const int rr) { if(l[root]==ll&&r[root]==rr)return ROOT->irt; if(mid[root]>=rr) { if(ROOT->lson)return search_irt(ROOT->lson,root<<1,ll,rr); return 0; } else if(mid[root]<ll) { if(ROOT->rson)return search_irt(ROOT->rson,root<<1|1,ll,rr); return 0; } else { int res=0; if(ROOT->lson)res+=search_irt(ROOT->lson,root<<1,ll,mid[root]); if(ROOT->rson)res+=search_irt(ROOT->rson,root<<1|1,mid[root]+1,rr); return res; } } int search_maxx(node *ROOT,const int root,const int ll,const int rr) { if(l[root]==ll&&r[root]==rr)return ROOT->maxx; if(mid[root]>=rr) { if(ROOT->lson)return search_maxx(ROOT->lson,root<<1,ll,rr); return 0; } else if(mid[root]<ll) { if(ROOT->rson)return search_maxx(ROOT->rson,root<<1|1,ll,rr); return 0; } else { int res=0; if(ROOT->lson)res=max(res,search_maxx(ROOT->lson,root<<1,ll,mid[root])); if(ROOT->rson)res=max(res,search_maxx(ROOT->rson,root<<1|1,mid[root]+1,rr)); return res; } } inline int ssearch_irt(node *ROOT,int a,int b) { int s=0; while(top[a]!=top[b]) { if(dep[top[a]]<dep[top[b]])swap(a,b); s=s+search_irt(ROOT,1,tid[top[a]],tid[a]); a=fa[top[a]]; } if(dep[a]>dep[b])swap(a,b); s=s+search_irt(ROOT,1,tid[a],tid[b]); return s; } inline int ssearch_maxx(node *ROOT,int a,int b) { int s=0; while(top[a]!=top[b]) { if(dep[top[a]]<dep[top[b]])swap(a,b); s=max(s,search_maxx(ROOT,1,tid[top[a]],tid[a])); a=fa[top[a]]; } if(dep[a]>dep[b])swap(a,b); s=max(s,search_maxx(ROOT,1,tid[a],tid[b])); return s; } inline char get_char() { char cu=getchar(); while(cu<'A'&&cu>'Z')cu=getchar(); return cu; } inline int getopt() { char a=get_char(),b=get_char(); if(a=='C') { if(b=='C')return 1; return 2; } else { if(b=='S')return 3; return 4; } } int main() { memset(head,-1,sizeof(head)); memset(son,-1,sizeof(son)); read(n),read(q); ini(1,1,n); for(rg int i=0;i<=2000000;i++)bin.push_back(&Q[i]); for(rg int i=1;i<=100000;i++)root[i]=new_node(); for(rg int i=1;i<=n;i++)read(v[i]),read(fr[i]); for(rg int i=1;i<n;i++) { int a,b;read(a),read(b); addb(a,b),addb(b,a); } dfs1(1,0,1),dfs2(1,1); for(rg int i=1;i<=n;i++) insert(root[fr[i]],1,tid[i],v[i]); for(rg int i=1;i<=q;i++) { int opt=getopt(),ll,rr;read(ll),read(rr); if(opt==1) { del(root[fr[ll]],1,tid[ll]); if(root[fr[ll]]==0)root[fr[ll]]=new_node(); fr[ll]=rr; insert(root[fr[ll]],1,tid[ll],v[ll]); } else if(opt==2) { v[ll]=rr; insert(root[fr[ll]],1,tid[ll],v[ll]); } else if(opt==3)print(ssearch_irt(root[fr[ll]],ll,rr)),putchar('\n'); else print(ssearch_maxx(root[fr[ll]],ll,rr)),putchar('\n'); } return flush(),0; }

結語

動態開點線段樹其實是一個很好用的技巧,就算加一個分配、回收記憶體的東西也不難,具體一定要用分配回收記憶體的題呢,我做到過,具體在這裡我就不介紹了