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;

}

 

你可能感兴趣的:(tar)