hdu 1556

hdu 1556 Color the ball

 

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556

 

题目大意:

 N个气球排成一排,从左到右依次编号1,2,3...N。要给气球涂N次色,每次涂色范围均为一个连续的区间。最后,要问每个气球被涂了多少次色。

 

思路:


 成段更新  、区间求和

 

看明题意,最直接的想法就是用线性数组做加法,最后统计。总的时间复杂度为O(N^2)+ O(N) 即 O(N^2)。而N的取值范围可到10,0000. 时间限制为3000ms. 果断超时。

然,使用线段树可以很好地解决这个问题。

 

这样就产生了如下两个问题:

 

① 怎么涂色呢 ? 

 

当我们给区间[fr,to)中的气球涂色的时候。我们可以利用树递归,从上至下,最后落实到树底的每一个气球,并将气球节点中的值加1。但是 !其实不一定要递归到底层。只要将树中特定的节点加1即可。那么是哪些节点呢 ?没错,就是每次递归过程中,节点对应区间能被 [fr,to)包含的第一个节点。

 

 ② 怎么查询气球的涂色次数呢 ?

 

 由于我们只是对特定的一些节点涂色,没有专门为最底层的气球节点涂色。所以在查询每个气球的涂色次数的时候,我们需要递归线段树—从树根 到 气球节点。这时候,你会发现,所有对气球i涂色操作中加1的节点都处于 从树根到气球i的这条路径 上。因此,这条路径上节点的总和就是气球i被涂色的次数!


画出样例2中 线段树的状态如图1所示:



算法步骤:

① 初始化线段树

② add函数为N个区间的气球涂色

③ query 函数 查询N个气球的涂色次数

 

算法复杂度:

① O(N)

② O(NlogN)

③ O(NlogN)

总的复杂度为O(NlogN)


codes:

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

int N, n;
const int maxn = 1<<17;
int dat[2*maxn-1];

void init() {
	n = 1;
	while( n < N )
		n *= 2;
	for(int i = 0 ; i < 2*n - 1 ; i ++)
		dat[i] = 0;
}

// fr,to 为查询范围   每次加一的操作只对树中的一个节点进行。这个
// 节点 的区间范围 就是 刚好能包含 区间 [fr,to) 的那个区间,从树根往下,一定能找到唯一的这样一个节点 
void add(int fr , int to , int l , int r , int k) {
	if(fr <= l && to >= r)
	{
		dat[k] ++;
		return;
	}
	else if(to <= l || fr >= r)
		return;
	else
	{
		add(fr,to, l, (l+r)/2 , 2*k+1);
		add(fr,to, (l+r)/2, r , 2*k+2);
	}
}

// 从树根往下递归直到 [fr,to)对应的点,所经过的路径的 所有节点 的 值的总和 就是 该点被涂过的次数 
int query(int fr , int to , int l , int r , int k) {
	// 不相交 
	if(to <= l || fr >= r)
		return 0;
	// 到了尽头 
	if(fr <= l && to >= r)
		return dat[k];
	else
		return dat[k] + query(fr,to,l,(l+r)/2,2*k+1) + query(fr,to,(l+r)/2,r,2*k+2);
}


int main() {
	while(scanf("%d",&N) != EOF)
	{
		if( N == 0)
			break;
		init();
		int fr,to;
		for(int i = 0 ; i < N ; i ++)
		{
			scanf("%d%d",&fr,&to);
			add(fr-1,to,0,N,0);
		}
		printf("%d",query(0,1,0,N,0));
		for(int i = 1 ; i < N ; i ++)
			printf(" %d",query(i,i+1,0,N,0));
		printf("\n");
		
	}
	return 0;
}

运行结果:Accept

Time  : 781 MS  

Memory: 1256 K  


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值