线段树
- 多段区间,求重合次数最多的区间或是给一个查询点判断该点在这些区间中存在的次数。
- 多条线段,求线段覆盖长度,重合记为一次。
1、思路:
非线段树:先对首尾排序(首尾端点没有差异),然后按顺序遍历结点,(L,+1)操作,(R,-1)操作。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 using namespace std; 5 #define NUM 5 6 7 typedef struct _guest 8 { 9 int arrival; 10 int leave; 11 }guest; 12 13 bool cmp(const int& i, const int& j) 14 { 15 return abs(i) < abs(j); 16 } 17 18 void GuestNumMax(guest* gs, int len) 19 { 20 vector<int> times; 21 for (int i = 0; i < len; ++i) 22 { 23 times.push_back(+gs[i].arrival); 24 times.push_back(-gs[i].leave); 25 } 26 sort(times.begin(), times.end(), cmp); 27 int n = 0, m = 0; 28 for (int i = 0; i < times.size(); ++i) 29 { 30 if (times[i] >= 0) 31 n++; 32 else 33 n--; 34 m = max(n, m); 35 } 36 cout << "The most num of guest is " << m << "!" << endl; 37 } 38 39 int main() 40 { 41 guest gs[NUM]; 42 gs[0].arrival = 1; 43 gs[0].leave = 2; 44 gs[1].arrival = 2; 45 gs[1].leave = 5; 46 gs[2].arrival = 3; 47 gs[2].leave = 7; 48 gs[3].arrival = 4; 49 gs[3].leave = 6; 50 gs[4].arrival = 7; 51 gs[4].leave = 8; 52 GuestNumMax(gs, NUM); 53 }
线段树:每个节点保存出现次数,然后按需要查询点按路径求和。左儿子表示的区间为[left,mid],右儿子表示的区间为[mid+1,right]。
1 #include <iostream> 2 using namespace std; 3 4 struct segmentree 5 { 6 int left, right; 7 int num; 8 }st[100]; 9 int segments[5][2] = {{1, 2},{2, 5},{3, 7},{4, 6},{7,8}}; 10 11 void Build(int idx, int left, int right) 12 { 13 st[idx].left = left; 14 st[idx].right = right; 15 if (right == left) 16 { 17 st[idx].num = 0; 18 return; 19 } 20 int mid = (left + right) / 2; 21 Build(idx*2+1, left, mid); 22 Build(idx*2+2, mid + 1, right); 23 st[idx].num = 0; 24 } 25 26 void Insert(int idx, int start, int end) 27 { 28 if (st[idx].left == start && st[idx].right == end) 29 { 30 st[idx].num++; 31 return; 32 } 33 if (st[idx].right == st[idx].left) return; 34 int mid = (st[idx].left + st[idx].right) / 2; 35 if (end <= mid) 36 Insert(idx * 2 + 1, start, end); 37 else if (start > mid) 38 Insert(idx * 2 + 2, start, end); 39 else 40 { 41 Insert(idx * 2 + 1, start, mid); 42 Insert(idx * 2 + 1, mid + 1, end); 43 } 44 }
2、思路:
非线段树:先对首尾排序(按首端点排序,如果首端点相等,再按尾端点排序),然后依次线段分析,每次线段的首尾端点与上一次迭代的尾端点比较。三种情况:下一个线段与前一个线段分离,下一个首端点在上一个尾端点的左侧,下一个尾端点在上一个尾端点的左侧,然后更新长度和尾端点。
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 #define NUM 3 5 6 typedef struct _line 7 { 8 int start; 9 int end; 10 }line; 11 12 bool cmp(const line& i, const line& j) 13 { 14 if (i.start < j.start) 15 return true; 16 else if (i.start > j.start) 17 return false; 18 else 19 return i.end < j.end; 20 } 21 22 void LengthSum(line* ls, int len) 23 { 24 sort(ls, ls + len, cmp); 25 int start, end, sum = 0; 26 for (int i = 0; i < len; ++i) 27 { 28 if (i == 0 || ls[i].start > end) 29 { 30 start = ls[i].start; 31 end = ls[i].end; 32 sum += end - start; 33 } 34 else if (ls[i].end > end) 35 { 36 sum += ls[i].end - end; 37 end = ls[i].end; 38 } 39 } 40 cout << "the length of lines is " << sum << " !" << endl; 41 } 42 43 int main() 44 { 45 line ls[NUM]; 46 ls[0].start = 1; 47 ls[0].end = 3; 48 ls[1].start = 2; 49 ls[1].end = 4; 50 ls[2].start = 5; 51 ls[2].end = 6; 52 LengthSum(ls, NUM); 53 }
线段树:左儿子表示的区间为[left,mid],右儿子表示的区间为[mid,right];每个区间标记是否覆盖,总长度就对覆盖的区间求和。
1 #include <iostream> 2 using namespace std; 3 #define LL(x) ((x) << 1 + 1) 4 #define RR(x) ((x) << 1 + 2) 5 6 struct seg_tree 7 { 8 int left, right; 9 bool isCover; 10 }st[10]; 11 int segment[3][2] = {{1, 3},{2, 4},{5, 6}}; 12 13 void Build(int idx, int left, int right) 14 { 15 st[idx].left = left; 16 st[idx].right = right; 17 if (right - left == 1) 18 { 19 st[idx].isCover = false; 20 return; 21 } 22 int mid = (left + right) / 2; 23 Build(idx*2+1, left, mid); 24 Build(idx*2+2, mid, right); 25 st[idx].isCover = false; 26 } 27 28 void Insert(int idx, int start, int end) 29 { 30 if (st[idx].isCover) return; 31 if (st[idx].left == start && st[idx].right == end) 32 { 33 st[idx].isCover = true; 34 return; 35 } 36 int mid = (st[idx].left + st[idx].right) / 2; 37 if (end <= mid) 38 Insert(idx*2+1, start, end); 39 else if (start >= mid) 40 Insert(idx*2+2, start, end); 41 else 42 { 43 Insert(idx*2+1, start, mid); 44 Insert(idx*2+2, mid, end); 45 } 46 } 47 48 int Count(int idx) 49 { 50 if (st[idx].isCover) 51 return st[idx].right - st[idx].left; 52 else if (st[idx].right - st[idx].left == 1) 53 return 0; 54 return Count(idx*2+1) + Count(idx*2+2); 55 } 56 57 void main() 58 { 59 Build(0, 0, 10); 60 for (int i = 0; i < 3; ++i) 61 Insert(0, segment[i][0], segment[i][1]); 62 printf("The cover length is %d\n", Count(0)); 63 }