Poj 2352 解题报告

来源:互联网 发布:动画师生存手册 知乎 编辑:程序博客网 时间:2024/05/20 18:03

Stars

Time Limit: 1000MS

Memory Limit: 65536K

Description

Astronomers often examine star maps where stars are represented by points on a plane and each star has Cartesian coordinates. Let the level of a star be an amount of the stars that are not higher and not to the right of the given star. Astronomers want to know the distribution of the levels of the stars. 


For example, look at the map shown on the figure above. Level of the star number 5 is equal to 3 (it's formed by three stars with a numbers 1, 2 and 4). And the levels of the stars numbered by 2 and 4 are 1. At this map there are only one star of the level 0, two stars of the level 1, one star of the level 2, and one star of the level 3. 

You are to write a program that will count the amounts of the stars of each level on a given map.

Input

The first line of the input file contains a number of stars N (1<=N<=15000). The following N lines describe coordinates of stars (two integers X and Y per line separated by a space, 0<=X,Y<=32000). There can be only one star at one point of the plane. Stars are listed in ascending order of Y coordinate. Stars with equal Y coordinates are listed in ascending order of X coordinate. 

Output

The output should contain N lines, one number per line. The first line contains amount of stars of the level 0, the second does amount of stars of the level 1 and so on, the last line contains amount of stars of the level N-1.

Sample Input

5

1 1

5 1

7 1

3 3

5 5

Sample Output

1

2

1

1

0

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed.

Source

Ural Collegiate Programming Contest 1999

题意大致上就是给你一张地图以及上面的点,让你求出每个点左下方点的个数,以此作为该点的值,最后输出各值的个数。

由于给出的数据是按照y的升序给出的,所以只要按照x进行处理即可。

求当前第i个点的level值,只要统计在它前面的所有x小于等于该点的个数。由此,就不难想到了利用线段树来解决此题。

首先是建树操作,源码如下:

void build(int u,int l,int r)

{

     node[u].left=l;

     node[u].right=r;

     node[u].num=0;

     if(l==r)return;

     build(u<<1,l,(l+r)>>1);

     build((u<<1)+1,((l+r)>>1)+1,r);

     return;

}

对于每个点首先进行查询操作,改点坐标为(xy),只需查询1…x的总和计可,源码如下

int get(int u,int l,int r)

{

    if(l==node[u].left&&r==node[u].right)

    return node[u].num;

    if(l>(node[u].left+node[u].right)>>1)

    return get((u<<1)+1,l,r);

    else if(r<=(node[u].left+node[u].right)>>1)

    return get(u<<1,l,r);

    else 

return

get((u<<1)+1,((node[u].left+node[u].right)>>1)+1,r)+get(u<<1,l,(node[u].left+node[u].right)>>1);

}

最后,还要对节点的值进行更新操作,源码如下:

void add(int u,int l)

{

     node[u].num++;

     if(node[u].left==node[u].right)return;

     if(l>(node[u].left+node[u].right)>>1)

     add((u<<1)+1,l);

     else

     add(u<<1,l);

     return;

}

此外,鉴于本题所求均为从1开始的,所以还可以用一种更方便的解法:树状数组,每步进行查询与更新的操作,显然速度更快。但是,使用树状数组一定要注意,树状数组必须从1开始,而x的范围是从零开始的,所以要先把x自加1后再进行操作。

使用树状数组的一大好处是无需像线段树一样首先建树,同时也更节省空间。数组中第i个数表示从第i-k+1i的和。将i表示成二进制,pi末尾零的个数,那么k即为2^p

事实上,有一种更简单的方法计算k

k=i&(i^(i-1))

我们假设该二进制数一共有n位,末尾零的个数为p,那么从后以0记起第p位为1,对于i-1而言,第p位为0,0…p-1位为1,而p+1到第n-1位均与i相同,则ii-1异或之后,0…p位均为1p+1..n-1均为0。再与i进行“与”操作便只剩下第p位是1了。

这是查询操作的源码(改成非递归版当然更好)

int get(int x)

{

    if(x<=0)return 0;

    return s[x]+get(x-lowbit(x));

}

以及更新部分

void add(int x)

{

     if(x>MAX)return;

     s[x]++;

     add(x+lowbit(x));

}

lowbit就是在求k