线段树 成段更新

来源:互联网 发布:js 字符串转对象 编辑:程序博客网 时间:2024/05/21 15:39

线段树就是许多线段组成的,并对每条线段进行操作,线段树的节点满足二叉树,所以某个节点的左右儿子是 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 4000002int 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;}

	
				
		
原创粉丝点击