[Swust OJ 746]--点在线上(线段树解法及巧解)
题目链接:http://acm.swust.edu.cn/problem/746/
Time limit(ms): 1000 Memory limit(kb): 65535
fate是一个数学大牛,热衷于各种数学问题.一次toshio,lo和fate玩了一个很简单的游戏.
在一条长40000的数轴上toshio说了M条线段的位置,每条线段给了头和尾的坐标,每条线的坐标都小于等于40000.
由lo发起N个提问,提问任意说一个点的坐标,要fate说出这个点在多少条线段上.
Description
两个整数M和N(0 < N,M <= 40000)
以下M行,每行两个整数b[i],e[i](0 <= b[i] < e[i] <= 40000)表示第i条线段的两个坐标.(注:点b[i]在线段上,但e[i]不在线段上.)
接下来N行,每行一个整数,表示lo询问的点的坐标.
Input
N行输出
对于lo询问的N个点,每个点分别在几条线段上.
Output
1
2
3
4
5
6
7
8
9
|
4 3
1 5
2 6
3 7
4 8
2
5
9
|
Sample Input
1
2
3
4
|
2
3
0
|
Sample Output
这道题可以说是经典的线段树的题目(关于线段树的用法可以看看这里:http://www.cnblogs.com/zyxStar/p/4562917.html)
直接上线段树的代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /************************线段树******************************/ 2 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 const int maxn = 40010; 7 #define rep(i,a,b) for(int i=a;i<b;i++) 8 9 struct node{ 10 int left, right, mid, val; 11 }interval[maxn << 2]; 12 13 int n, m, x, y; 14 15 //线段树的构建 16 void build(int left, int right, int k){ 17 interval[k].left = left; 18 interval[k].right = right; 19 interval[k].mid = (left + right) / 2; 20 interval[k].val = 0; 21 if (left == right) 22 return; 23 build(left, interval[k].mid, 2 * k); 24 build(interval[k].mid + 1, right, 2 * k + 1); 25 } 26 27 //插入操作寻找[left,right]更新数据 28 void insert(int left, int right, int k){ 29 if (interval[k].left == left && interval[k].right == right){ 30 interval[k].val++; 31 return; 32 } 33 if (right <= interval[k].mid) 34 insert(left, right, 2 * k); 35 else if (left > interval[k].mid) 36 insert(left, right, 2 * k + 1); 37 else{ 38 //覆盖重合拆分成两个区间更新数据 39 insert(left, interval[k].mid, 2 * k); 40 insert(interval[k].mid + 1, right, 2 * k + 1); 41 } 42 } 43 44 //查找算法 45 int query(int left, int right, int k, int pos){ 46 if (left == right) 47 return interval[k].val; 48 if (pos <= interval[k].mid) 49 return interval[k].val + query(left, interval[k].mid, 2 * k, pos); 50 else 51 return interval[k].val + query(interval[k].mid + 1, right, 2 * k + 1, pos); 52 } 53 54 int main(){ 55 while (cin >> n >> m){ 56 build(0, maxn, 1); 57 rep(i, 0, n){ 58 cin >> x >> y; 59 insert(x, --y, 1); 60 } 61 rep(i, 0, m){ 62 cin >> x; 63 cout << query(0, maxn, 1, x) << endl; 64 } 65 } 66 return 0; 67 }
当然游戏到此还没有结束,发现这道题还有一种巧妙的解法
像这样只需要开一个数组point,对a,b线段操作的时候,把端点point[a++],point[b++]就可以了,
到时point[i]+=point[i-1]就可以了,这样就把每个点在几条线上同步了
比如point[c]+=point[a],从内部区间向外扩散这样就可以表示了,具体的还是看代码理解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 int point[40010]; 3 int main(){ 4 int n, m, i, a, b; 5 scanf("%d%d", &n, &m); 6 for (i = 1; i <= n; i++){ 7 scanf("%d%d", &a, &b); 8 point[a]++; 9 point[b]--; 10 } 11 for (i = 1; i <= 40010; i++) 12 point[i] += point[i - 1]; 13 for (i = 1; i <= m; i++){ 14 int x; 15 scanf("%d", &x); 16 printf("%d\n", point[x]); 17 } 18 return 0; 19 }
如果这是你所爱的,就不要让自己后悔~~~