首页 > 代码库 > poj 2352 Stars 数星星 详解

poj 2352 Stars 数星星 详解

题目:

poj 2352 Stars 数星星

题意:已知n个星星的坐标。每个星星都有一个等级,数值等于坐标系内纵坐标和横坐标皆不大于它的星星的个数。星星的坐标按照纵坐标从小到大的顺序给出,纵坐标相同时则按照横坐标从小到大输出。 (0 <= x, y <= 32000) 要求输出等级0到n-1之间各等级的星星个数。

分析:

  这道题不难想到n平方的算法,即从纵坐标最小的开始搜,每次找它前面横坐标的值比它小的点的个数,两个for循环搞定,但是会超时。

  所以需要用一些数据结构去优化,主要是优化找 横坐标比它小的点的个数 这里可以用线段树维护。我设sumv的意义是,从L到R(L,R代表横坐标的值)范围内的横坐标的个数。它的左儿子是L到M内的个数,右儿子是M+1到R内的个数。叶节点当L=R是,就是横坐标为L的点的个数。最后用vis数组记录每个等级的数量即可。

  我一开始的做法是先输入完,然后直接构造所有节点的线段树,但是这样就不能同时判断纵坐标的大小,调试了好久,发现是错误的。

  由于题目是按照纵坐标的大小,从小到大,同时横坐标从小到大给出的,因此,在一边输入的时候一个点一个点构造线段树就 不用 判断纵坐标的大小。所以只要维护一个横坐标的线段树,在输入的时候一边输,一边查询,一边更新,这样就可以了。在更新的时候,插入数据的方法是从根节点往下一点点更新,查询的方法就是普通的线段树查询。但还有一个小问题:在输入的时候你不能知道线段树的大小是多少,因为你不知道在整个数据中最大的那个横坐标。那我们只好慷慨一点,范围直接用maxn(32000),虽然处理小数据时浪费了点空间,但其实没事的。

  查询和更新的时间复杂度都是logn。总算法的时间复杂度O(nlogn)。

 

附上代码:

 

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int maxn=40010;
 5 
 6 int X[maxn],Y[maxn],n,sumv[maxn*4];
 7 int vis[maxn];
 8 int p;
 9 void update(int o,int L,int R)
10 {
11     if(L==R) sumv[o]++;
12     else 
13     {
14         int M=(L+R)/2;
15         if(p<=M) update(o*2,L,M);
16         else update(o*2+1,M+1,R);
17         sumv[o]++;
18     }
19 }
20 
21 int ans;
22 void query(int o,int L,int R)
23 {
24     if(R<=p) ans+=sumv[o];
25     else if(p<L) return;
26     else
27     {
28         int M=(L+R)/2;
29         query(o*2,L,M);
30         query(o*2+1,M+1,R);
31     }
32 }
33 
34 int main()
35 {
36     cin>>n;
37     for(int i=1;i<=n;i++)
38     {
39         cin>>X[i]>>Y[i];
40         p=X[i],ans=0;
41         query(1,0,maxn);
42         vis[ans]++;
43         update(1,0,maxn);
44     }
45     for(int i=0;i<=n-1;i++) cout<<vis[i]<<endl;
46     return 0;
47 }

 

poj 2352 Stars 数星星 详解