线段树学习总结
Posted arrogant-hierarch
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树学习总结相关的知识,希望对你有一定的参考价值。
线段树用途:
用于区间修改与求和:
区间修改:
修改l到r之间的值 , 遍历线段树 , 若某个子节点l<=L && R<=r ,
则在该节点上标记修改的值 , 访问该节点及他的子节点时再将标记下传;
线段树可持久化:
核心: 永远不修改节点上的值 , 只会新建节点 ;
实现:
每当修改值的时候 , 新建一个跟节点 , 将他与上一个版本的线段树中未修改的值相连 ,
对于修改的值新建一个节点 , 当访问到新节点及他的子节点时再动态开点;
zkw非递归线段树:
建树:
int t[N*2];
for(int i=1;i<=n;i++) t[i+N]=a[i];
单点修改(将下标x上的值改为y):
t[x+=N]=y;
for(x>>=1;x;x>>=1)
{
t[x]=t[x<<1]+t[x<<1|1];
}
区间求和:
int ans=0;
for(l+=N-1,r+=N;l^r^1;l>>=1,r>>=1) // l^r^1用来判断左右节点的父节点是不是同一个节点
{
/* 如果当前区间的左端点在他父节点的左子节点上就要加上他的父节点的右子节点,
相当于左端点包括了右字节点所包括的数*/
if(!(l^1)) ans+=t[l^1];
/* 如果当前区间的右端点在他父节点的右子节点上就要加上他的父节点的左子节点*/
if(r^1) ans+=t[r^1];
}
特点:
不能标记下传;
可以用来代替堆优化Dijkstra;
带修改的二维区间求和:
建树:
建立一棵范围为[1,n]的线段树,再在这棵线段树的每个节点上建立一棵范围为[1,m]的线段树;
题目:
【CF260E】 Dividing Kingdom: 9!暴力枚举状态 + 线段树可持久化判断当前状态对错;
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 100010; 9 10 int n; 11 int a[9]; 12 int cntx, cnty; 13 int x[N], y[N]; 14 int sum[N * 20], ls[N * 20], rs[N * 20], root[N], tot; 15 vector<int> ins[N]; 16 17 struct Node 18 { 19 int x, y; 20 }t[N]; 21 22 void update(int& q, int last, int l, int r, int v) 23 { 24 q = ++tot; 25 ls[q] = ls[last]; 26 rs[q] = rs[last]; 27 sum[q] = sum[last] + 1; 28 29 if(l == r) return; 30 int mid = (l + r) >> 1; 31 if (v <= mid) update(ls[q], ls[last], l, mid, v); 32 else update(rs[q], rs[last], mid + 1, r, v); 33 } 34 35 int kth(int p, int q, int l, int r, int v) // 找第p版本线段树到第q版本线段树中差值为v的节点 36 { 37 if (l == r) return l; 38 39 int s = sum[ls[q]] - sum[ls[p]]; 40 int mid = (l + r) >> 1; 41 if (s >= v) return kth(ls[p], ls[q], l, mid, v); 42 else return kth(rs[p], rs[q], mid + 1, r, v - s); 43 } 44 45 int query(int q, int l, int r, int L, int R) // 求区间和 46 { 47 if ((!q) || (L <= l && r <= R)) return sum[q]; 48 49 int res = 0; 50 int mid = (l + r) >> 1; 51 if (L <= mid) res += query(ls[q], l, mid, L, R); 52 if (R > mid) res += query(rs[q], mid + 1, r, L, R); 53 return res; 54 } 55 56 57 int S(int l1, int r1, int l2, int r2) // 求子矩阵 点[l1,l2] 到 点[r1,r2] 之间的点的数量 58 { 59 return query(root[r1], 1, cnty, l2, r2) - query(root[l1 - 1], 1, cnty, l2, r2); 60 } 61 62 bool solve() 63 { 64 int l = 1, r = cntx, ans = 0; // 求在左边的那条竖线 65 while (l <= r) 66 { 67 int mid = (l + r) >> 1; 68 if (sum[root[mid]] >= a[0] + a[1] + a[2]) 69 { 70 ans = mid; 71 r = mid - 1; 72 } 73 else l = mid + 1; 74 } 75 if (ans == 0 || sum[root[ans]] != a[0] + a[1] + a[2]) return false; // 没找到这条线 76 int p1 = ans; 77 78 l = 1, r = cntx, ans = 0; // 求在右边的那条竖线 79 while (l <= r) 80 { 81 int mid = (l + r) >> 1; 82 if (sum[root[mid]] >= a[0] + a[1] + a[2] + a[3] + a[4] + a[5]) 83 { 84 ans = mid; 85 r = mid - 1; 86 } 87 else l = mid + 1; 88 } 89 if (ans == 0 || sum[root[ans]] != a[0] + a[1] + a[2] + a[3] + a[4] + a[5]) return false; // 没找到这条线 90 int p2 = ans; 91 92 int posx1 = kth(root[0], root[p1], 1, cnty, a[0]); // 找上面那条横线 93 while (S(1, p1, 1, posx1) != a[0] || S(p1 + 1, p2, 1, posx1) != a[3] || S(p2 + 1, cntx, 1, posx1) != a[6]) 94 { 95 if (S(1, p1, 1, posx1) != a[0]) return false; // 找不到 96 ++posx1; 97 } 98 99 int posx2 = kth(root[0], root[p1], 1, cnty, a[0] + a[1]); // 找下面那条横线 100 while (S(1, p1, posx1+1, posx2) != a[1] || S(p1 + 1, p2, posx1 + 1, posx2) != a[4] || S(p2 + 1, cntx, posx1 + 1, posx2) != a[7]) 101 { 102 if (S(1, p1, posx1 + 1, posx2) != a[1]) return false; // 找不到 103 ++posx2; 104 } 105 106 printf("%.10lf %.10lf %.10lf %.10lf", x[p1] + 0.5, x[p2] + 0.5, y[posx1] + 0.5, y[posx2] + 0.5); 107 return true; 108 } 109 110 bool com(Node a, Node b) 111 { 112 return a.x == b.x ? a.y < b.y : a.x < b.x; 113 } 114 115 int main() 116 { 117 scanf("%d", &n); 118 for (int i = 1; i <= n; i++) 119 { 120 scanf("%d%d", &t[i].x, &t[i].y); 121 x[++cntx] = t[i].x; 122 y[++cnty] = t[i].y; 123 } 124 for (int i = 0; i < 9; i++) scanf("%d", &a[i]); 125 126 sort(x + 1, x + 1 + cntx); 127 cntx = unique(x + 1, x + 1 + cntx) - (x + 1); 128 sort(y + 1, y + 1 + cnty); 129 cnty = unique(y + 1, y + 1 + cnty) - (y + 1); 130 sort(t + 1, t + 1 + n, com); 131 132 for (int i = 1; i <= n; i++) // 哈希 133 { 134 t[i].x = lower_bound(x + 1, x + cntx + 1, t[i].x) - x; 135 t[i].y = lower_bound(y + 1, y + cnty + 1, t[i].y) - y; 136 ins[t[i].x].push_back(t[i].y); 137 } 138 139 for (int i = 1; i <= cntx; i++) // 建可持久化线段树 140 { 141 root[i] = root[i - 1]; 142 for (int j = 0; j < ins[i].size(); j++) 143 { 144 update(root[i], root[i], 1, cnty, ins[i][j]); 145 } 146 } 147 148 int loop = 362880; 149 while (loop--) 150 { 151 next_permutation(a, a + 9); // 全排列 152 if (solve()) return 0; 153 } 154 155 puts("-1"); 156 }
以上是关于线段树学习总结的主要内容,如果未能解决你的问题,请参考以下文章