题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1541
问题描述
Stars
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 12877 Accepted Submission(s): 5048
Problem 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
Source
Ural Collegiate Programming Contest 1999
------------------------------------------------------------
题意
给定第一象限的n个坐标,定义一个坐标的level为横坐标小于等于自己且纵坐标也小于等于自己的坐标的个数,计算输出level = 0,1,2,…,n-1的坐标的个数
------------------------------------------------------------
思路
之前用了树状数组做的这道题,详见HDU 1541 Stars(树状数组)。其实可以用树状数组做的题都可以用线段树做,本文就是本题的线段树解法。
注意到题目输入保证纵坐标y是递增的,那么对于刚刚读入的一个点,我们只要计算出在此之前有多少个点的横坐标也小于等于这个点的横坐标,就能计算出该点的level。直接暴力求解的复杂度为O(n^2).
线段树的思路是:
假设一个数组a[0:XRANGE](XRANGE是最大可取的横坐标值), 其中a[i]表示横坐标等于a[i]的点的个数,那么题目就转化为求区间和问题,即一个横坐标为x的点的level = sum(a[0:x])-1(-1表示去掉自己)。因为频繁的区间求和操作,考虑基于数组a构造线段树tree.
又由于后面输入的点不可能对前面输入的点的level有贡献,所以不用等到所有点都输入完毕后再构造线段树,而是可以将线段树初始化为全0,然后每读入一个点坐标,就用update函数更新线段树。
线段树的query(区间查询)和update(单点修改)复杂度均为logXRANGE,故总复杂度为O(nlogXRANGE).
再有就是本题的2个坑点:
1. x和n的范围比题干中所述的高一个数量级
2. 多组测试数据输入
------------------------------------------------------------
代码
#include<cstdio>
#include<cstring>
const int XMAX = 320005, XRANGE = 320000, NMAX = 150005;
int ans[NMAX], tree[4*XMAX]; // ans[i]: level==i的点个数,tree[root]表示在[tl, tr]之间的a[j]的数量
void update(int root, int tl, int tr, int x) // 读入一个横坐标为x的点,线段树相应节点+1,即单点修改
{
if (tl > x || tr < x)
{
return;
}
if (tl == tr)
{
tree[root] += 1;
return;
}
int mid = (tl + tr)/2;
if (x <= mid)
{
update(2*root+1, tl, mid, x);
}
else
{
update(2*root+2, mid+1, tr, x);
}
tree[root] += 1;
}
int query(int root, int tl, int tr, int x) // 查询横坐标<=x的点的数量,即求[0,x]的线段和
{
if (x < tl)
{
return 0;
}
if (tr <= x)
{
return tree[root];
}
int mid = (tl + tr) / 2;
if (x <= mid)
{
return query(2*root+1, tl, mid, x);
}
else
{
return query(2*root+1, tl, mid, x) + query(2*root+2, mid+1, tr, x);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("hdu1541.txt", "r", stdin);
#endif
int n, i, x, y;
while (scanf("%d", &n) != EOF)
{
memset(ans, 0, sizeof(ans));
memset(tree, 0, sizeof(tree));
for (i=0; i<n; i++)
{
scanf("%d%d", &x, &y);
update(0, 0, XRANGE, x);
ans[query(0, 0, XRANGE, x)-1]++; // query把横坐标=x的点自己也算在内,要去掉
}
for (i=0; i<n; i++)
{
printf("%d\n", ans[i]);
}
}
return 0;
}