HDU 1541 Stars【树状数组】

本文介绍如何使用树状数组解决星图问题,通过统计星图中每个星星的级别来计算不同级别的星星数量,详细解释了树状数组的实现和优化思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

原题:HDU 1541

题目意思就是说,给你一张star maps,每个star有自己的level,level是这样计算的:(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.)统计这个star左下角star的个数,就是这个star的level。现在要你总计图中level从0到N-1的star分别有多少个?

题目输入描述中明确告诉我们,输入的坐标是按Y的升序、如果Y相等,则按X的升序。所以我们发现我们可以完全忽略Y,只要统计小于本身X的star个数,就是level了。现在我们用一个数组a[]统计X坐标为i的个数(增加一个既a[i]++;);同时对每个star计算的得level。

这样对于X坐标为j的star,他得level为:

level=a[0]+a[1]+a[2]+…+a[j]

如果这样计算的话,每个level最坏时间要用O(m),计算n-1个level,这样要用O(mn)的时间复杂度,对于这题的数据而言,这无疑是无法接受的。我们发现,对于计算level,这是个查询区间问题sum[0,j], 如果没有元素的变更(既数组a是不变的),我们完全可以存储sum[0,k](k=0,2,……),然后对任意给定的查找区间[i,j],都可以方便的用ans=sum[1,j]-sum[1,i-1],当然这只是没有元素改变的情况下的比较优化的解法.那么对于有元素变更的问题是否有更高效的方法呢?这让我们想到的树状数组。

树状数组所针对问题:已知数组a[],元素个数为n,现在更改a中的元素,要求得新的a数组中i到j区间内的和(1<=i<=j<=n).

这题是比较简单和明显的树状数组,我们先不讨论如何实现树状数组。只告诉你树状数组现在有2个操作:

(1)    add(int i,int val) //将第i个元素更改val

(2)    sum(int i) //求前i项和

所以这题我们的伪代码可以如下实现:

for(i=0;i<n;i++)

{

1.    sacnf(x,y);

2.    cnt[sum(x+1)]++;

3.    add(x+1,1);

4.     

}

5.print:cnt[0,n-1];

上面因为树状数组是从1开始计数的,而坐标有可能为0,所以我们统一都加1;sum(x+1)就是计算当前star的level值,所以当前level个数加1;add(x+1,1)操作表示对当前元素更改(既+1);最后打印cnt数组。

树状数组的实现及其思想

那么树状数组是如何实现快速对数组更新和区间求和的呢?

关于树状数组的实现和思想,这个再不细说,参考下面资料:

http://baike.baidu.com/view/1420784.htm

http://www.cnblogs.com/yykkciwei/archive/2009/05/08/1452889.html

代码:

//============================================================================
// Name        : Stars.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN=32010;

int n,c[MAXN],cnt[MAXN];

inline int lowbit(int x)
{
	return x&(-x);
}

void add(int i,int val)
{
	while(i<MAXN)
	{
		c[i]+=val;
		i+=lowbit(i);
	}
}

int sum(int i)
{
	int s=0;
	while(i>0)
	{
		s+=c[i];
		i-=lowbit(i);
	}
	return s;
}

int main() {
	int x,y;
	while(scanf("%d",&n)!=EOF)
	{
		memset(c,0,sizeof(c));
		memset(cnt,0,sizeof(cnt));
		for(int i=0;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			cnt[sum(x+1)]++;
			add(x+1,1);

		}
		for(int i=0;i<n;i++)
		{
			printf("%d\n",cnt[i]);
		}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值