HDU--1556 -- Color the ball [树状数组] [线段树]

2024-06-10 13:32

本文主要是介绍HDU--1556 -- Color the ball [树状数组] [线段树],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

Color the ball

 

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5864    Accepted Submission(s): 3122

Problem Description
N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?
Input
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。
Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。
Sample Input
  
3 1 1 2 2 3 3 3 1 1 1 2 1 3 0
Sample Output
  
1 1 1 3 2 1

 

Code:

 

这一题真难为本姐姐啊难过

早上徒弟问我的时候顺便给了别人正确的代码,如下

按照树状数组去理解, 总觉得和树状数组有些不一样,原来是变形了==,晕

 

/*
这题是插段求点。有点变化但是是一样的。
插线段的话就在线段左端点加1,在右端点后面减一。
这样求和得到的就是这个点的染色次数了。
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=100010;
int c[MAXN];
int n;
int lowbit(int x)//求最小幂,即x的最大的能用2的n次方表示的因子 
{
return x&(-x);
}
void add(int i,int val)//更新数据,将含有该元素的后面的每一位都更新 
{
while(i<=n)
{
c[i]+=val;
i+=lowbit(i);
}
}
int sum(int i)
{
int s=0;
while(i>0) //该位运算后,跳到前面-lowbit的地方,因为从这以后的元素和存在该位上 
{
s+=c[i];
i-=lowbit(i);
}
return s;
}
int main()
{
int a,b;
while(scanf("%d",&n),n)
{
memset(c,0,sizeof(c));
for(int i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,1);	//插线段法 
add(b+1,-1);
}
for(int i=1;i<n;i++)//按格式输出 
printf("%d ",sum(i));
printf("%d\n",sum(n));
}
return 0;
}


后来用了线段树解题

虽说代码比较长吧,思想还是有利于进步的

在这个这个线段树中,如果找到一个和输入范围相同的区间,次数value就++, 即使下面有子树也不用赋值

最后只要将各个子树和各个层的双亲相加,就得到了总染色次数

前几次数组分配太小了,,郁闷!

后来输出次数的时候用的是找到最后一个叶子节点的数组下标, 一直-- ,直到减到最开始一个叶子节点为止

但是! 有一种情况是叶子结点可能在一个双亲结点和一个叶子结点之间,比如说n=6时, 姐姐我加上各种条件判断, 均不对啊

无奈只好采用类似于中序遍历的方法替代之, 从根节点开始把每层父亲结点的值都累加在一起,加到最后就是叶子结点的值, 一点不同就是父亲节点不用输出

OK, 今天一天全都荒废睡觉

 

#include"stdio.h"
#include"string.h"
int n;
typedef struct interval_tree_node
{
int value;
int lside,rside;
}interval_tree_node;
interval_tree_node tree[400010];
void tree_build(int n,int left,int right)//建立一个叶子节点为n的线段树 
{
tree[n].lside = left;
tree[n].rside = right;
tree[n].value = 0;
if(left == right) return;
int mid = (left+right)>>1;
tree_build(n<<1,left,mid);
tree_build(n<<1|1,mid+1,right);	
}
void colour(int start,int a,int b)//根据输入范围染色并计数 
{
//	printf("start:%d  a:%d b:%d ",start,a,b);
if(tree[start].lside>=a && tree[start].rside<=b)
{
tree[start].value += 1;
//	printf("final: start=%d value=%d\n",start,tree[start].value);
return;
}	
int mid = (tree[start].lside+tree[start].rside)>>1;	
if(b<=mid) 	
colour(start<<1,a,b);	
else if(a>mid)
colour(start<<1|1,a,b);		 
else	//染色范围左右子树都有 
{
colour(start<<1,a,mid);
colour(start<<1|1,mid+1,b);		
}
}
void forprint(int start)
{
tree[start].value += tree[start/2].value;
if(tree[start].lside==tree[start].rside) 
{
printf("%d",tree[start].value);
//	count--;
if(tree[start].lside!=n) printf(" ");
return;
}
//	int mid = (tree[start].lside+tree[start].rside)>>1;
forprint(start<<1);
//	start = start/2;
//	count -= tree[start].value;
forprint(start<<1|1);
}	
int main()
{
int i,a,b;
while(scanf("%d",&n),n)
{		
tree_build(1,1,n);				
for(i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
colour(1,a,b);
}			
forprint(1);
printf("\n");			
}
return 0;
}

这篇关于HDU--1556 -- Color the ball [树状数组] [线段树]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048296

相关文章

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while