HDU -1556 Color the ball -线段树-成段更新

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

题意:

对于n个气球编号分别为123……n进行上色,每次上色为一个区间a~b,问n次操作后,每个气球分别被上色多少次。

分析:

用线段树做,只需要每次更新都适当地做标记,最后将所有标记都下沉至叶子结点,此时,叶子结点的标记值便是该气球被上色的次数,直接输出各个叶子结点的值即可。

说一下我对标记下沉的理解,以此题为例,假设有如下数据:

5

1 3 ——①

1 2 ——②

1 1 ——③

1 4 ——④

1 5 ——⑤

那么操作流程分别为

建树:


可得pos_rt[0]=8,pos_rt[1]=9, pos_rt[2]=5, pos_rt[3]=6, pos_rt[4]=7

而后更新:

① [1 3]做标记,sign[2]++



② [1 2]做标记,[1 3]标记下沉:sign[4]+=sign[2],sign[5]+=sign[2],sign[4]++;sign[2]=0;



③ [1 1]做标记,[1 2]标记下沉: sign[8]+=sign[4],sign[9]+=sign[4],sign[8]++;sign[4]=0;



④ [1 4]做标记,标记[1 3][4 4]



⑤ [1 5]做标记



此时标记全都做完了,然后对所有标记进行下沉到叶子结点



输出每个叶子结点的标记,及每个气球的上色次数。

代码:

 #include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
  using namespace std;
 int pos_rt[100001];//用来记录气球编号对应的下标rt
 int sign[400000];//各rt的标记
 int size;//气球的编号个数
 void _build(int l,int r,int rt)//建树(其实我的建树只是用来查找记录气球编号的rt的)
  {
     if(l==r)
     {
         pos_rt[size++]=rt;//找到叶子结点时,记录下rt,以便输出
         return;
     }
     int m=(l+r)>>1;
     _build(l,m,rt<<1);
     _build(m+1,r,rt<<1|1);
  }
 void _downUpDate(int rt)//标记下沉
  {
     if(sign[rt])//当有标记时
     {
         sign[rt<<1]+=sign[rt];//左子树标记更新
         sign[rt<<1|1]+=sign[rt];//右子树标记更新
         sign[rt]=0;//删除当前rt的标记
     }
  }
 void _upDate(int L,int R,int l,int r,int rt)//用以更新下沉标记
  {
     if(L<=l&&r<=R)//区间包含,当前区间的标记+1
     {
         sign[rt]++;
         return;
     }
     _downUpDate(rt);//当前rt被用到,故下沉标记到左右子树
     int m=(l+r)>>1;
     if(L<=m)
         _upDate(L,R,l,m,rt<<1);
     if(R>m)
         _upDate(L,R,m+1,r,rt<<1|1);
 
  }
 void _pushDown(int l,int r,int rt)//所有标记下沉至叶子结点
  {
     if(l==r)//叶子结点回溯
         return;
     _downUpDate(rt);//非叶子结点下沉标记
     int m=(l+r)>>1;
     _pushDown(l,m,rt<<1);
     _pushDown(m+1,r,rt<<1|1);
  }
 int main()
  {
     int n,a,b,i;
     while(scanf("%d",&n),n)
     {
         size=0;
         memset(pos_rt,0,sizeof(pos_rt));
         memset(sign,0,sizeof(sign));
         _build(1,n,1);
         for(i=0;i<n;i++)
         {
             scanf("%d%d",&a,&b);
             _upDate(a,b,1,n,1);
         }
         _pushDown(1,n,1);
         for(i=0;i<size-1;i++)//直接输出各叶子结点的标记的值,对应的rt已用pos_rt数组记录
             printf("%d ",sign[pos_rt[i]]);
         printf("%d\n",sign[pos_rt[size-1]]);
     }
     return 0;
  }
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值