题目:
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 }
时间: 2024-10-06 22:47:33