树状数组
经典入门题,只要搜索数星星就能在各个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; }