数星星(树状数组的理解与应用)

数星星问题

前导

树状数组的原理详见如下链接
链接: 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与原生数组的含义密切相关,没注意到就会导致题目出错

看完的宝子们如果觉得有帮助麻烦点个赞啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帆与翔的网工之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值