【POJ No. 2352】数星星 Stars
北大OJ 题目地址
【题意】
星星由平面上的点表示,星星的等级为纵横坐标均不超过自己的星星数量(不包括自己)。下图中,
5号星的等级为3(纵横坐标均不超过5号星的星星有3颗:1、2和4号)。2和4号星的级别是1。
在该地图上有一颗0级星、两颗1级星、一颗2级星和一颗3级星。计算给定地图上每个级别的星星数量。
【输入输出】
输入:
第1行包含星星的数量N (1≤N ≤15000)。以下N 行描述星星的坐标,每行都包含两个整数X 、Y (0≤X ,Y ≤32000)。平面上的一个点只可以有一颗星星。以Y 坐标升序输入,在Y 坐标相等时以X 坐标升序输入。
输出:
输出包含N 行,第1行包含0级的星星数量,第2行包含1级的星星数量……最后一行包含N- 1级的星星数量。
【样例】
提示: 数据量巨大,这里使用scanf而不是cin来读取数据,避免超出时间限制。
【思路分析】
每颗星星的等级都为它左下方的星星个数。输入所有星星(按照y 升序,若y 相等,则x 升序)的坐标,依次输出等级0~n -1的星星数量。
输入样例的地图如下图所示,图中星星旁边的数字为输入顺序,1号星的左下没有星星,等级为0;2号星的左边有1颗星星,等级为1;3号星的左边有2颗星星,等级为2;4号星的左下有1颗星星,等级为1;5号星的左边有3颗星星,等级为3。因此等级为0的有1个,等级为1的有2个,等级为2的有1个,等级为3的有1个,等级为4的有0个。
本题看似二维数据,实际上输入数据已经按照y 升序,也就是说,读到一个点时,当前点的y 坐标肯定大于或等于已经输入的y 坐标。
如果y 坐标相等,则x 坐标肯定大于已经输入的x 坐标,所以每次只要计算x 坐标比当前点小的点就行了。该问题的本质是统计x 坐标前面星星的数量,是前缀和问题。因为数据量较大,暴力穷举会超时,所以可以借助树状数组解决。
注意:给的点坐标从0开始,树状数组下标从1开始(0的位置不可用),所以需要在输入x 坐标时加1处理。
【算法设计】
① 依次输入每一个坐标x 、y ,执行x ++。
② 计算x 的前缀和sum(x ),将其作为该星星的等级,用ans[]数组累计该等级的数量。
③ 将树状数组中x 的数量加1。
【算法实现】
#include<cstdio>
using namespace std;
#define maxn 32010
#define lowbit(x) (x)&(-x)
int ans[maxn],c[maxn];//等级统计,每个值的数量
int n;
void add(int i,int val){//将第i个元素增加val,其后继也要增加
while(i<=maxn){//是x点的范围,注意不是星星的个数n
c[i]+=val;
i+=lowbit(i);//i的后继(父结点)
}
}
int sum(int i){//前缀和
int s=0;
while(i>0){
s+=c[i];
i-=lowbit(i);//i的前驱
}
return s;
}
int main(){
scanf("%d",&n);
int x,y;
for(int i=0;i<n;i++){
scanf("%d%d",&x,&y);
x++;
ans[sum(x)]++;
add(x,1);//x的数量c[x]增1
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
return 0;
}