[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
 
直接上线段树的代码
 
 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 }
View Code

 

当然游戏到此还没有结束,发现这道题还有一种巧妙的解法

 

 

像这样只需要开一个数组point,对a,b线段操作的时候,把端点point[a++],point[b++]就可以了,

到时point[i]+=point[i-1]就可以了,这样就把每个点在几条线上同步了

比如point[c]+=point[a],从内部区间向外扩散这样就可以表示了,具体的还是看代码理解

 

 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 }
View Code

 

posted @ 2015-06-09 12:11  繁夜  阅读(313)  评论(0编辑  收藏  举报