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个气球编号分别为1,2,3……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;
}