數據結構-線段樹
數據結構圖
eg:1-10的線段樹(區間裏面的數代表左右邊界值,區間下面的數代表在tree數組中的下標)
基本功能實現思路及代碼
0.基礎結構體
1 struct N 2 { 3 int l,r,w; //左邊界 右邊界 區間維護值 4 int lazy; //懶值 5 }tree[4*n];
註意:這裏的tree要開4倍n的大小,原因是開的區間中有一些是沒被利用的如上圖.(懶值的作用下面會說)
1.建樹-build函數
更新當前區間左右邊界+葉子節點處理(賦值)+往左右子節點擴展+更新
eg:區間維護的值為區間和(l,r,k初始值分別為1,n,0)(k為區間下標)
1 void build(int l,int r,int k) 2 { 3 tree[k].l=l,tree[k].r=r;//更新當前區間左右邊界 4 if(l==r) //葉子節點處理 5 { 6 scanf("%d",&tree[k].w); 7 return ; 8 } 9 int mid=(l+r)>>1; 10 build(l,mid,k*2+1); //擴展左子區間 11 build(mid+1,r,k*2+2); //擴展右子區間 12tree[k].w=tree[2*k+1].w+tree[2*k+2].w; //更新 13 }
2.單點查詢-ask_point函數
葉子區間處理(若到達葉子區間則找到值)+前進區間判斷(該詢問左子區間還是右子區間)
eg:區間維護的值為區間和(查詢第x個值)
1 int ask_point(int k,int x) 2 { 3 if(tree[k].l==tree[k].r) return tree[k].w; //葉子節點處理 4 int mid=(tree[k].l+tree[k].r)>>1; //前進區間 5 if(x<=mid) return ask_point(k*2+1,x); 6 else return ask_point(k*2+2,x); 7 }
3.單點修改-add_point函數
單點查詢+更新
eg:區間維護的值為區間和(第x個值加上y)
1 void add_point(int k,int x,int y) 2 { 3 if(tree[k].l==tree[k].r) //葉子區間處理 4 { 5 tree[k].w+=y; 6 return ; 7 } 8 int mid=(tree[k].l+tree[k].r)>>1; //前進區間 9 if(x<=mid) add_point(k*2+1,x,y); 10 else add_point(k*2+2,x,y); 11 tree[k].w=tree[k*2+1].w+tree[k*2+2].w; //更新 12 }
4.區間查詢-ask_interval函數
被包含區間處理(若區間被包含則直接貢獻該區間的值)+前進區間判斷(需不需要收集左子區間和右子區間的值)
eg:區間維護的值為區間和(查詢(a,b)區間的區間和)
1 int ask_interval(int k,int a,int b) 2 { 3 if(tree[k].l>=a&&tree[k].r<=b) return tree[k].w;//區間被包含 4 int mid=(tree[k].l+tree[k].r)>>1; 5 int sum=0; //用來收集左右子區間的值 6 if(a<=mid) sum+=ask_interval(k*2+1,a,b); 7 if(b>mid) sum+=ask_interval(k*2+2,a,b); 8 return sum; 9 }
5.區間修改-add_interval函數
這裏需要增加一個新概念-懶值
當一個節點區間被要修改區間包含時,只需要改這個節點區間的值就行了,不需要往下修改子區間的值,當需要用到該區間的子區間時才去修改該區間的子區間的值.這個優化的實現就需要用到懶值,懶值存的是子區間需要修改的值的累積值,當需要用到子區間時就把懶值分配下去-down函數.
這裏給出down函數寫法(把懶值分配給子區間)
1 void down(int k) 2 { 3 tree[k*2+1].lazy+=tree[k].lazy; 4 tree[k*2+2].lazy+=tree[k].lazy; 5 tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1); 6 tree[k*2+2].w+=tree[k].lazy*(tree[k*2+2].r-tree[k*2+2].l+1); 7 tree[k].lazy=0; 8 }
add_interval函數寫法是在區間查詢的基礎上加上了down函數的利用和更新代碼
eg:區間維護的值為區間和(給(a,b)區間的值加上x)
1 void add_interval(int k,int a,int b,int x) 2 { 3 if(tree[k].l>=a&&tree[k].r<=b) 4 { 5 tree[k].w+=x*(tree[k].r-tree[k].l+1); 6 tree[k].lazy+=x; 7 return ; 8 } 9 if(tree[k].lazy) down(k); //如果到這一步則表明需要用到該節點子區間的值 10 int mid=(tree[k].l+tree[k].r)>>1; 11 if(a<=mid) add_interval(k*2+1,a,b,x); 12 if(b>mid) add_interval(k*2+2,a,b,x); 13 tree[k].w=tree[k*2+1].w+tree[k*2+2].w; 14 }
6.lazy引入的影響
前面所過當要用到用有lazy值的節點的子區間時,需要把lazy分配下去,所以引入lazy後除了build函數其他功能函數都必須追加一個判斷 if(lazy存在) down(k);
下面給出具有以上5個基本功能的線段樹代碼(區間維護的值為區間和)
1 #include <cstdio> 2 #include <cmath> 3 #include <iostream> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 #include <string> 8 #include <utility> 9 #include <queue> 10 #include <stack> 11 #include <map> 12 #include <set> 13 const int INF=0x3f3f3f3f; 14 using namespace std; 15 16 const int MAX_N=100000; 17 struct N 18 { 19 int l,r,w; 20 int lazy; 21 }tree[4*MAX_N]; 22 23 void build(int l,int r,int k) 24 { 25 tree[k].l=l,tree[k].r=r; 26 if(l==r) 27 { 28 scanf("%d",&tree[k].w); 29 return ; 30 } 31 int mid=(l+r)>>1; 32 build(l,mid,k*2+1); 33 build(mid+1,r,k*2+2); 34 tree[k].w=tree[2*k+1].w+tree[2*k+2].w; 35 } 36 37 void down(int k) 38 { 39 tree[k*2+1].lazy+=tree[k].lazy; 40 tree[k*2+2].lazy+=tree[k].lazy; 41 tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1); 42 tree[k*2+2].w+=tree[k].lazy*(tree[k*2+2].r-tree[k*2+2].l+1); 43 tree[k].lazy=0; 44 } 45 46 int ask_point(int k,int x) 47 { 48 if(tree[k].l==tree[k].r) return tree[k].w; 49 if(tree[k].lazy) down(k); //只添加了該判斷 50 int mid=(tree[k].l+tree[k].r)>>1; 51 if(x<=mid) return ask_point(k*2+1,x); 52 else return ask_point(k*2+2,x); 53 } 54 55 void add_point(int k,int x,int y) 56 { 57 if(tree[k].l==tree[k].r) 58 { 59 tree[k].w+=y; 60 return ; 61 } 62 if(tree[k].lazy) down(k); //只添加了該判斷 63 int mid=(tree[k].l+tree[k].r)>>1; 64 if(x<=mid) add_point(k*2+1,x,y); 65 else add_point(k*2+2,x,y); 66 tree[k].w=tree[k*2+1].w+tree[k*2+2].w; 67 } 68 69 int ask_interval(int k,int a,int b) 70 { 71 if(tree[k].l>=a&&tree[k].r<=b) return tree[k].w; 72 if(tree[k].lazy) down(k); //只添加了該判斷 73 int mid=(tree[k].l+tree[k].r)>>1; 74 int sum=0; 75 if(a<=mid) sum+=ask_interval(k*2+1,a,b); 76 if(b>mid) sum+=ask_interval(k*2+2,a,b); 77 return sum; 78 } 79 80 void add_interval(int k,int a,int b,int x) 81 { 82 if(tree[k].l>=a&&tree[k].r<=b) 83 { 84 tree[k].w+=x*(tree[k].r-tree[k].l+1); 85 tree[k].lazy+=x; 86 return ; 87 } 88 if(tree[k].lazy) down(k); 89 int mid=(tree[k].l+tree[k].r)>>1; 90 if(a<=mid) add_interval(k*2+1,a,b,x); 91 if(b>mid) add_interval(k*2+2,a,b,x); 92 tree[k].w=tree[k*2+1].w+tree[k*2+2].w; 93 } 94 95 int main() 96 { 97 int n; 98 cin>>n; 99 build(1,n,0); 100 //該部分隨題意添加 101 return 0; 102 }View Code
數據結構-線段樹