题解 | #[NOIP2004]合唱队形#

数星星 Stars

https://ac.nowcoder.com/acm/problem/50428

题目中的坐标给出序列是一个很好的序列。因为我们只需要找该点左下角的星星,那么后面给出的点必然不符合左下角的设定,所以只需要判断给出该点之前有多少个星星在其左下角。更准确的说,只需要判断多少个星星的x坐标<=当前星星的x坐标即可。

不会树状数组,还没学。用线段树写了一遍,还是比较基础的。

主要思路就是对于每一个读入的点,搜索一遍x坐标组成的线段树中有多少个区间在他之前即可。比如读入的x坐标为7,假设之前读入的x坐标区间[1,10],那么第一个点的区间就是[1,10],然后依次往下二分成左右子树区间,显然[1,5],[6,7]为最终找到的组成答案的区间。加起来即可。

具体看代码,代码中有注释吧。

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e4 + 5e3 + 7,rr=3e4+2e3+7;//rr表示的是x的最大坐标,也就是所构建的线段树的最大区间的端点值。
int ans[N],n,x,y,cnt=0,tmp=0,val[rr<<2];//val表示区间的值,应该开rr*4的大小,因为区间最大的端点值就是x的最大坐标值。
void serch(int p,int l,int r,int x)
{
    if (r <= x)//判断条件应当是r<=x的时候,l不用判,因为只考虑某个区间在x的左边即可。
    {
        tmp += val[p]; return;
    }
    int mid = l + r >> 1;
    serch(p << 1,l,mid,x);//左边区间必搜
    if (x >= mid + 1)serch(p<<1|1,mid+1,r,x);//右边区间需要判断x是否在这个区间
}
void build(int p,int l,int r,int x)
{
    if (l==r)//代表找到了x这个点
    {
        val[p]++;
        return;
    }
    int mid = l + r >> 1;
    if (x <= mid)build(p << 1, l, mid, x);
    else build(p << 1 | 1, mid + 1, r, x);
    val[p] = val[p << 1] + val[p << 1 | 1];//回溯赋值
}
int main()
{
    cin >> n;
    for (int i=1;i<=n;i++)
    {
        tmp = 0;//这是不考虑0的等级。每次要清零。
        scanf("%d%d",&x,&y);
        if (x == 0) { ans[cnt++]++; continue;}//此处用于特判x=0的情况,cnt表示x=0的星星的个数
        serch(1,1,rr,x);
        ans[tmp + cnt]++;//要额外考虑cnt,所以这里是tmp+cnt
        build(1,1,rr,x);
    }
    for (int i = 0; i < n; i++)printf("%d\n",ans[i]);
    return 0;
}

那么就让我们一起愉快地AC吧~

全部评论
因为不能包括他自己这个点,所以是先搜索serch,再插入建树build
点赞 回复 分享
发布于 2023-11-13 16:24 湖南

相关推荐

01-16 18:34
四川大学 Java
欢迎加入AI:没有啥稳定不稳定,一切都源于业务快速发展还是收缩。我当年一开始去的央企,业务不赚钱,也贼卷,慢慢就开始优化了。。。
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
02-14 11:10
点赞 评论 收藏
分享
评论
3
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务