ural(Timus) 1028 Stars

树状数组

经典入门题,只要搜索数星星就能在各个OJ找到这个题目,不过不同OJ的输入和输出可能不同,但是题意是一样的,就是统计每个星星的等级

入门题详细说一下。首先对输入的星星进行排序,先按x坐标升序排序,x坐标相同的按y坐标升序排序,这样做是后面能使用树状数组的根本保证。由于这题,输入数据中就已经保证了是按y坐标升序输入若y坐标相同则按x坐标升序输入,所以不需要排序,注意,两种排序方法是一样的。下面就按本题的来讲

 

树状数组中是由原数组变化得到的,a是原数组,c是树状数组,并且数组是从下标1开始的,为什么从1开始是因为位运算和二进制的一些问题。在这里我们已经知道0<=x,y<=32000,所以我们的数组长度就是这么大。为什么呢?因为我们统计的是x的个数

a[x]表示坐标为x的星星有多少个,所以c[x]就是对应的一段和c[x]=a[x-2^k+1]……a[x]。我们要知道一个星星的等级也就是其左下方有多少个星星,那么我们先知道在其左边有多少个星星,再知道其下面有多少个星星,但是其下面有多少个星星是不用计算的,已经知道了,为什么,因为数据是按照y升序排序的(现在终于知道了为什么要排序了)。那么也就说,对于第i个星星,前面i-1个星星一定在其下方或者同一水平线上(其后面的星星一定在其上方或者同一水平线上,是一定不符合要求的不用考虑)。只要在这些星星中选出一些星星,它们的x比第i个星星的x小,那么就可以计数了。我们初始化话原数组a和树状数组均为0,a[x]表示到目前为止,横坐标为x的星星有多少个,注意这个,到目前为止。所以没读入一个星星,就可以去更新a数组,即对应的a[x]加1,同样的a数组的变化要对应到c数组的变化。只要不断读入星星的坐标,并不断更新c数组就可以了。我们可以发现,a数组只是帮组我们理解,根本不需要用到它,所以是不需要专门开辟一个a数组的


由于一个星星的等级只和它前面的星星有关,和后面的星星无关,所以我们已经可以一边读入数据一边计算每个星星的等级了!排序保证了y坐标是递增的,我们只要知道横坐标为1到x的星星的个数就是当前星星的等级。

由于坐标中有0,但是树状数组是 从1开始的,所以对于每个x都要加1后再操作,否则是错误的

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 15010
#define MAX 32010

int c[MAX],ans[N];

int lowbit(int n)
{ return n&(-n); }

int sum(int p)
{
    int ans=0;
    while(p)
    {
        ans += c[p];
        p -= lowbit(p);
    }
    return ans;
}

void add(int p ,int n ,int k)
{
    while(p<=n)
    {
        c[p] += k;
        p += lowbit(p);
    }
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(c,0,sizeof(c));
        memset(ans,0,sizeof(ans));

        for(int i=0; i<n; i++)
        {
            int x,y,level;
            scanf("%d%d",&x,&y);
            level=sum(x+1);
            //printf("level=%d\n",level);
            ans[level]++;
            add(x+1,MAX,1);
        }

        for(int i=0; i<n; i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值