线段树 成段更新

线段树就是许多线段组成的,并对每条线段进行操作,线段树的节点满足二叉树,所以某个节点的左右儿子是 2*rt,和2*rt+1;

举例说明:已知线段[2,5] [4,6] [0,7];求点2,4,7分别出现了多少次

在[0,7]区间上建立一棵满二叉树:(为了和已知线段区别,用【】表示线段树中的线段)

                                               【0,7】
                               /                                            \
                     【0,3】                                           【4,7】
                  /               \                                    /                \
       【0,1】             【2,3】                       【4,5】               【6,7】
         /      \                 /      \                     /      \                   /      \
【0,0】 【1,1】    【2,2】       【3,3】    【4,4】   【5,5】      【6,6】   【7,7】

三条已知线段插入过程:

[2,5]

--[2,5]与【0,7】比较,分成两部分:[2,3]插到左儿子【0,3】,[4,5]插到右儿子【4,7】

--[2,3]与【0,3】比较,插到右儿子【2,3】;[4,5]和【4,7】比较,插到左儿子【4,5】

--[2,3]与【2,3】匹配,【2,3】记录+1;[4,5]与【4,5】匹配,【4,5】记录+1

[4,6]

--[4,6]与【0,7】比较,插到右儿子【4,7】

--[4,6]与【4,7】比较,分成两部分,[4,5]插到左儿子【4,5】;[6,6]插到右儿子【6,7】

--[4,5]与【4,5】匹配,【4,5】记录+1;[6,6]与【6,7】比较,插到左儿子【6,6】

--[6,6]与【6,6】匹配,【6,6】记录+1

[0,7]

--[0,7]与【0,7】匹配,【0,7】记录+1

插入过程结束,线段树上的记录如下(红色数字为每条线段的记录n):

                                               【0,7】
                                                    1
                               /                                            \
                     【0,3】                                           【4,7】
                         0                                                     0
                 /                 \                                     /                 \
       【0,1】                 【2,3】                【4,5】                【6,7】
            0                           1                          2                         0
          /    \                      /      \                     /     \                    /      \
【0,0】 【1,1】           【2,2】   【3,3】    【4,4】   【5,5】  【6,6】      【7,7】
     0            0            0            0            0            0           1            0

询问操作和插入操作类似,也是递归过程,略

2——依次把【0,7】 【0,3】 【2,3】 【2,2】的记录n加起来,结果为2

4——依次把【0,7】 【4,7】 【4,5】 【4,4】的记录n加起来,结果为3

7——依次把【0,7】 【4,7】 【6,7】 【7,7】的记录n加起来,结果为1

练习: http://acm.nyist.net/JudgeOnline/code.php?runid=260199  士兵杀敌三。

#include<stdio.h>
#include<string.h>
#define maxn 4000002
int sum=0;
struct tree
{
    int left,right,n;
    int mid()
    {
        return (left+right)>>1;
    }
};
tree a[maxn];
void buildtree(int s,int t,int n)
{
    a[n].left=s;
    a[n].right=t;
    int mid=a[n].mid();
    if(s==t) return ;
    buildtree(s,mid,2*n);
    buildtree(mid+1,t,2*n+1);
}
void insert(int s,int t,int step,int h)//插入:
{
    if(s==a[step].left&&t==a[step].right)
    {
        a[step].n+=h;
        return ;
    }
    if(a[step].left==a[step].right) return ;//当前线段没有儿子,插入结束;
    int mid=a[step].mid();
    if(mid>=t) insert(s,t,step*2,h);//如果中点在T的右边,插到左儿子
    else if(mid<s) insert(s,t,step*2+1,h);//在S的左边,插到右儿子。
    else//否则,中点在ST中间,各分到左右儿子里。
    {
        insert(s,mid,step*2,h);
        insert(mid+1,t,step*2+1,h);
    }
}
void count(int s,int t,int step)//访问
{
    if(a[step].n!=0) sum+=a[step].n;
    if(a[step].left==a[step].right) return;
    int mid=a[step].mid();
    if(mid>=t) count(s,t,step*2);
    else if(mid<s) count(s,t,step*2+1);
    else
    {
        count(s,mid,step*2);
        count(mid+1,t,step*2+1);
    }
}
int main()
{
    int m,b,c,i,t,h;
    char ss[6];
    while(~scanf("%d%d",&c,&m))
     {
         for(i=0;i<=m;i++)
           a[i].n=0;
         buildtree(1,m,1);
         while(c--)
         {
             scanf(" %s",ss);
             if(strcmp(ss,"ADD")==0)
             {
                 scanf("%d%d%d",&b,&t,&h);
                 insert(b,t,1,h);
              }
              if(strcmp(ss,"QUERY")==0)
              {
                  sum=0;
                  scanf("%d",&t);
                  count(t,t,1);
                  printf("%d\n",sum);
              }
          }
     }
     return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值