線段樹(區間樹)
阿新 • • 發佈:2018-11-25
-
線段樹
-
定義及作用
-
建樹
-
基本操作
一.定義及作用
線段樹是一種二叉搜尋樹,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。使用線段樹可以快速的查詢某一個節點在若干條線段中出現的次數,時間複雜度為O(logN)。而未優化的空間複雜度為2N。
二.建樹
1.先定義一個結構體以便每個節點能存東西
struct node
{
int l;//區間左端點
int r;//區間右端點
int w;//區間儲存的值
}tree[400001];//開四倍大小
2.遞迴建樹
void build(int k,int l,int r)//建樹 { tree[k].l=l,tree[k].r=r; if(tree[k].l==tree[k].r) //遞到葉子節點的時候歸 { scanf("%d",&tree[k].w); //輸入各葉子節點的值 return; //出遞迴 } int m=(l+r)/2; //準備生成左右子樹 build(k*2,l,m); //遞迴左子樹 build(k*2+1,m+1,r); //左子樹遞迴完遞迴右子樹 tree[k].w=tree[k*2].w+tree[k*2+1].w; //向上更新各區間值就是pushup(k) }
三、線段樹的基本操作
向上更新
void pushup(int k)
{
tree[k].w=tree[k*2].w+tree[k*2+1].w; //父親的值根據其子孫的值確定
}
向下更新
void pushdown(int k) { tree[k*2].f+=tree[k].f; //將更改的單位值傳遞給左兒子 tree[k*2+1].f+=tree[k].f; //將更改的單位值傳遞給右兒子 tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1); //更新左兒子儲存的值 tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1); //更新右兒子儲存的值 tree[k].f=0; //向下更新完畢取消標記值 }
1.單點查詢
void ask_point(int k,int x) { if(tree[k].l==tree[k].r) //當查到葉子節點時 { ans=tree[k].w; //ans為全域性變數傳遞查詢的值 return ; //出遞迴 } if(tree[k].f) pushdown(k); //向下更新 int m=(tree[k].l+tree[k].r)/2; //取區間中值,準備遞迴左右子樹查詢 if(x<=m) ask_point(k*2); //若查詢的單點為區間中值或在區間左邊則遞迴左子樹 else ask_point(k*2+1); //否則遞迴右子樹 }
2.單點更新
void change_point(int k,int x,int v)
{
if(tree[k].l==tree[k].r)
{
tree[k].w+=v;
return;
}
if(tree[k].f) pushdown(k);
int m=(tree[k].l+tree[k].r)/2;
if(x<=m) change_point(k*2);
else change_point(k*2+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;
}
3.區間查詢
void ask_interval(int k,int a,int b)
{
if(tree[k].l>=a&&tree[k].r<=b) //區間在a,b之間
{
ans+=tree[k].w;
return;
}
if(tree[k].f) pushdown(k);
int m=(tree[k].l+tree[k].r)/2;
if(a<=m) ask_interval(k*2); //若本區間中值等於a或在a右邊(說明左邊還能遞迴)
if(b>m) ask_interval(k*2+1); //若本區間中值在b左邊(說明右邊還能遞迴)
}
4.區間更新(未解釋懶標記)
void change_interval(int k)
{
if(tree[k].l>=a&&tree[k].r<=b)
{
tree[k].w+=(tree[k].r-tree[k].l+1)*y;
tree[k].f+=y;
return;
}
if(tree[k].f) pushdown(k);
int m=(tree[k].l+tree[k].r)/2;
if(a<=m) change_interval(k*2);
if(b>m) change_interval(k*2+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;
}