HDU 1541 Stars(线段树)

题目来源: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. 

https://i-blog.csdnimg.cn/blog_migrate/fe1a27fe685322035ebcd08664e5becb.jpeg

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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值