HDU1556 Color the ball

本文详细介绍了一种高效的数据结构——线段树,通过一个具体的编程问题示例解释了线段树的构建、更新及查询操作。文章还提供了完整的代码实现,并附带了进一步学习资源。

Color the ball

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

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

 

#include <iostream>

#include <cstring>

#include <cstdio>

#include <algorithm>

using namespace std;

#define N 100008

#define lid (id<<1)//

#define rid (lid|1)//

typedef long long LL;

struct node

{

    int l,r;//左右边界

    LL sum;//表示的和

    int lazy;//延迟标记

}tr[N*4];

int val=1;//添加的值

 

void build(int l,int r,int id)//从上到下建立一棵树

{

    tr[id].l = l; tr[id].r = r;//左右边界赋值

    tr[id].sum = 0;//和赋值

    tr[id].lazy = 0;//延迟标记清零

    if( l == r )

        return ;//结束条件别忘了

    int mid=(tr[id].l+tr[id].r)>> 1;//中点

    build(l,mid,lid);//左边

    build(mid+1,r,rid);//右边

}

 

void push_up(int id)//向上传递

{

    tr[id].sum = tr[lid].sum + tr[rid].sum;//一个结点的值等于它左右两边的和

}

 

void push_down(int id)//向下分解

{

    int m=tr[id].r-tr[id].l+1;//类似这个结点的长度

    if(tr[id].lazy)//延迟标记不为0

    {

        tr[lid].lazy+=tr[id].lazy;//延迟标记传给左边

        tr[rid].lazy+=tr[id].lazy;//延迟标记传给右边

        tr[lid].sum+=tr[id].lazy*(m-(m>>1));

        //左边的值等于它本身加上 添加的值乘长度的一半(也可以理解为它本身的长度)

        tr[rid].sum+=tr[id].lazy*(m>>1);

        //右边同理 只不过是剩下的一半

        tr[id].lazy=0;//注意 分解之后 延迟标记一定要变回0

    }

}

 

void updata(int a,int b,int id)//更新区间内的值

{

    if(tr[id].l>=a&&tr[id].r<=b)//恰好是要更新的范围

    {

        tr[id].lazy+=val;//延迟标记加上更新值

        tr[id].sum+=val*(tr[id].r-tr[id].l+1);//此结点值变为它本身加上它所包含的长度

        return ;

    }

    else

    {

        push_down(id);//向下分解

        int mid=(tr[id].l+tr[id].r)>> 1;//中点

        if(a>mid)//区间起点大于中点

        {

            updata(a,b,rid);//全部在右边 更新右

        }

        else if(b<=mid)//区间终点小于等于终点

        {

            updata(a,b,lid);//全部在左边 更新左

        }

        else//两边都有

        {

            updata(a,b,rid);

            updata(a,b,lid);

        }//都更新

    }

    push_up(id);//向上传递

}

 

LL query(int a,int b,int id)//查询区间值

{

    if(tr[id].l>=a&&tr[id].r<=b)//恰好是要查询的区间

        return tr[id].sum;//直接返回值

    else

    {

        push_down(id);//查询时也别忘了分解

        int mid=(tr[id].l+tr[id].r)>> 1;//中点

        if(a>mid)//全在右 查询右

        {

            return query(a,b,rid);

        }

        else if(b<=mid)//全在左 查询左

        {

            return query(a,b,lid);

        }

        else//都有 返回两边的和

        {

            return query(a,b,rid)+query(a,b,lid);

        }

    }

}

 

int main()

{

    int n;

    while(cin>>n)

    {

        if(n==0)

            break;

        build(1,n,1);

        for(int i=1;i<=n;i++)

        {

            int a,b;

            cin>>a>>b;

            updata(a,b,1);

        }

        for(int i=1;i<n;i++)

        {

            printf("%I64d ",query(i,i,1));

        }

        printf("%I64d\n",query(n,n,1));//别忘了long long注意下格式

    }

    return 0;

}

 

 顺便推荐一篇写的比较详细的线段树知识点的文章。

线段树入门(二):http://www.cnblogs.com/shadowland/p/5870354.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值