模板原型:
解决零散数点在已知线段上的出现次数。思想是将线段用长线覆盖,将长线转化成线段树。用权值记录各个数点出现的次数,最后进行查询。代码解释见注释。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAXN = 3e4 + 10; 5 int n, m, l, r; //长度n,线段数m 6 7 8 struct line { 9 int left, right; //left:左边界 right:右边界 10 int n; //该节点的权值 11 } a[MAXN]; 12 13 void buildt(int l, int r, int step){ 14 a[step].left = l; //建立当前节点的左边界赋值 15 a[step].right = r; //建立当前节点的右边界赋值 16 a[step].n = 0; //初始化当前节点的权值 17 if(l == r) {return; } //如果是叶子节点,不进行下面的递归建树操作 18 buildt(l, (l + r) / 2, step * 2); //递归建立左结点 19 buildt((l + r) / 2 + 1, r, step * 2 + 1); //递归建立右结点 20 } 21 22 void dfs(int step){ 23 cout << step << " " //当前节点下标 24 << a[step].left << " " //结点左边界 25 << a[step].right << " " //结点右边界 26 << a[step].n << endl; //结点对应权值 27 if(a[step].left == a[step].right) return;//若为叶子结点,结束当前节点深搜 28 dfs(step * 2); //递归搜索左结点 29 dfs(step * 2 + 1); //递归搜索右结点 30 return ; 31 } 32 33 void insert(int s, int t, int step){ 34 if(s == a[step].left && t == a[step].right) { 35 ++ a[step].n; //插入的线段匹配则该条线段的记录 +1 36 return ; //插入操作成功,返回 37 } 38 39 if(a[step].left == a[step].right) //如果当前线段没有子节点,返回 40 return ; 41 42 int mid = (a[step].left + a[step].right) / 2; //二分思想确立中值mid 43 44 if(mid >= t) { //如果中值在t的右边 45 insert(s, t, step * 2); //则插入到左儿子 46 47 } else if (mid < s) { //如果中值在s的左边 48 insert(s, t, step * 2 + 1);//则插入到右儿子 49 50 } else { 51 insert(s, mid, step * 2);//否则中点将线段划分,左端到中点部分放入左结点 52 insert(mid + 1, t, step * 2 + 1);//中点到右端部分放入右结点 53 } 54 return ; 55 } 56 57 int main(){ 58 cout << MAXN << endl; 59 cin >> n >> m; 60 buildt(0, n, 1); 61 //dfs(1); 62 for(int i = 0; i < m; i ++ ) { 63 cin >> l >> r; 64 insert(l, r, 1); 65 } 66 dfs(1); 67 return 0; 68 }
线段树(build,insert,dfs操作)
时间: 2024-10-30 03:23:50