動態開點線段樹(多棵線段樹)的記憶體分配與回收
前言
線段樹,是一個很好用的能支援O(logn)區間操作的資料結構,隨著做一些稍微煩一點的題,有時候會發現有些情況要開一個數組的線段樹,更有甚者要樹套樹,而在很多情況下線段樹就不能把所有點都開滿了(否則會MLE記憶體超限),於是就出現了線段樹的動態開點寫法
基本思想
與普通的線段樹相同,動態開點線段樹只是一開始每一個節點都沒有,insert的時候,如果遇到節點是空的,那麼就宣告這個點,詢問的時候只訪問詢問的區間中非空節點,這樣一來,時間複雜度沒有問題還是O(
但是這麼做當遇到樹套樹的時候,空間複雜度就會炸的很慘,為O(有過值的節點的數量*
那怎麼優化呢,delete的時候如果遇到這個節點的兒子都是空的,那麼就刪掉這個點,回收這個點的記憶體,這樣空間複雜度就能優化到O(有值節點數量最多時的節點數*
例題
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;
}
結語
動態開點線段樹其實是一個很好用的技巧,就算加一個分配、回收記憶體的東西也不難,具體一定要用分配回收記憶體的題呢,我做到過,具體在這裡我就不介紹了