数星星问题
前导
树状数组的原理详见如下链接
链接: https://blog.csdn.net/2302_77698668/article/details/132653153
问题详情
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。
如果一个星星的左下方(包含正左和正下)有 k 颗星星,就说这颗星星是 k 级的。
例如,上图中星星 5 是 3 级的(1,2,4 在它左下),星星 2,4 是 1 级的。
例图中有 1 个 0 级,2 个 1 级,1 个 2 级,1 个 3 级的星星。给定星星的位置,输出各级星星的数目。
换句话说,给定 N 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
输入格式
第一行一个整数 N,表示星星的数目;
接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x坐标增序给出。
输出格式
N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N−1 级的星星的数目。
数据范围
1≤N≤15000
0≤x,y≤32000【坐标从 0 开始,可能存在边界问题,需要在输入的时候处理】
输入样例:
5
1 1
5 1
7 1
3 3
5 5
输出样例:
1
2
1
1
0
问题分析
关键语句分析
不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x坐标增序给出。
这句话是问题分析的关键,看图
假设我们对 5 号点进行等级计算,根据上面的话可以推导出 6, 7, 8 ····的纵坐标一定大于 5 号点,而 1, 2,3, 4号点的纵坐标一定是小于等于 5 号点,所以要求解5 号点的等级,就要统计横坐标 小于等于 5 号点横坐标 所有点的数目
我们在这里用数组 a [ N ] 代表 横坐标为 x 的点个数的集合
方法判断
根据之前的举例,这道题要求解前缀和,要在数组上进行修改
毫无疑问–》》树状数组
代码
具体的理解全部在注释里面哈
#include<iostream>
using namespace std;
const int N = 32001;//由于后面横坐标平移了,那么上限也+1;
int tr[N];//树状数组,统计横坐标小于等于 x 的所有点的数目
//可以理解为前缀和
int ans[N];//存储答案,即每个等级的星星数目
int n;
int lowerbit(int x){
return x & -x;
}
void add(int x,int v){
for(int i=x;i<=N;i+=lowerbit(i)){//为什么这里要遍历 N 次,而不是 n+1 呢?
//因为这里记录的是数值的个数
//比如说有 100 个数,但是有些数的下标是 2000 ,所以这里的 N 应当是数值的最大值
//当然我们可以在输入时统计出横坐标 x 的最大值
//但是为了方便就直接用上限了
//总结下来就是上限的取值与原生数组的性质有关
tr[i]+=v;
}
}
int query(int x){
int res=0;
for(int i=x;i>=1;i-=lowerbit(i)){
res+=tr[i];
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
x++;//由于涉及到前缀和,这里我们将所有坐标向右平移1位
//这样的操作不会影响到结果
add(x,1);
ans[query(x)]++;
}
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;//这里从 1 开始是因为所有 x 的下标从 1开始
return 0;
}
总结
对于树状数组的使用,首先要确定的是原生数组代表了什么,进而通过已有的结论构建起树状数组,其次在 add函数
当中,我们的上限N与原生数组的含义密切相关,没注意到就会导致题目出错
看完的宝子们如果觉得有帮助麻烦点个赞啦