HDU1556

来源:互联网 发布:java注释 编辑:程序博客网 时间:2024/05/20 04:13

维护每次被涂抹的区间

Color the ball

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

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

 

这道题可以算是脑洞要开始开启的题目,一看到题目给的信息点,需要访问的最大长度以及需要操作的次数,就知道这个题不用线段树必须TLE,否则就是出题人有问题,但是这道题和前三道的线段树又不同,如果还是按照以前的方法建树再到每个区间的叶子节点标记一定还是TLE,所以这时可以建树再使用cover标记去标记完全包含区间的结点或者是可以一边建树一边更新。 

#include<iostream>#include<stdio.h>#include<algorithm>#include<string.h>using namespace std;#define Max 100005int tree[Max*4];int sum;void build(int p,int l,int r,int a,int b)//函数中参数包括根节点p,树的左端点l,右端点r;区间的左端点a,右端点b{if(a<=l&&b>=r) {tree[p]++;return;}//找到一个区间(a,b)完全包含的结点,并给该结点+1,代表这个区间内的气球都被涂抹了一次,最后递归出口int mid=(l+r)>>1;if(b<=mid) build(p<<1,l,mid,a,b);//如果mid>=b,则在左子树中查找对应结点else if(a>mid) build(p<<1|1,mid+1,r,a,b);//如果a>mid,在右子树中查找else//否则说明该区间跨越两个区间,需要在两个区间同时查找{build(p<<1,l,mid,a,mid);build(p<<1|1,mid+1,r,mid+1,b);}}int Find(int p,int l,int r,int x)//Find函数需要实现的操作就是在建好树以后实现加和得出每个叶子节点的值{if(l==r) return tree[p];int mid=(l+r)>>1;if(x<=mid)  sum=sum+tree[p]+Find(p<<1,l,mid,x);else  sum=sum+tree[p]+Find(p<<1|1,mid+1,r,x);return sum;}int main(){int N,a,b,t;while(scanf("%d",&N)!=EOF){memset(tree,0,sizeof(tree));//每次开始操作前记得把线段树清零,由于能输入多组数据if(N==0) break;t=N;while(t--){scanf("%d%d",&a,&b);build(1,1,N,a,b);//每次输入建树更新区间,相比暴力减少了时间复杂度}sum=0;printf("%d",Find(1,1,N,1));if(N>=2) for(int i=2;i<=N;i++) { sum=0; printf(" %d",Find(1,1,N,i));//这个代码最终耗时的大头是输出每个叶子节点的时候,但是整个代码中只有这一次需要遍历,相比暴力还是相当优化的 }printf("\n");}return 0;}


0 0
原创粉丝点击