线段树一:修改点的值求任意区间的值

来源:互联网 发布:rime输入法linux 编辑:程序博客网 时间:2024/05/12 22:07

强烈推荐:这篇文章------------------------(完全版)线段树。

线段树的作用:解决区间计算问题。

例如:记录一个区间的最值(最大或最小值)和总量,并在区间的插入、删除、修改中维护这些最值和总量。

线段树是一颗二叉树。记为T(a,b),a,b表示顶点T为区间[a,b]。区间长度b-a记为L。递归定义T[a,b]:

若区间长度L>=1,区间[a,(a+b)/2]为T的左孩子,区间((a+b)/2,b]为T的右孩子

如图为区间[1,10]的线段树:

线段树的基本操作:

(1)建立线段树T(a,b)。

(2)将区间[c,d]插入到线段树T(a,b)。

(3)将区间[c,d]从线段树T(a,b)中删除。

(4)对线段树进行动态维护。

以下代码转载自:himdd的文章。

线段树的建立:

int Pushup(int root) //把当前结点的信息更新到父节点 {   sum[root]=sum[root<<1]+sum[root<<1|1];}void Build(int L,int R,int root){   if(L==R)     {   scanf("%d",&sum[root]);        return ;    }    int mid=(L+R)>>1;    Build(Lson); //左孩子     Build(Rson); //右孩子     Pushup(root); }

上面代码构造的树为,我没有画完整,但是已经能够体现问题了。

后面的数相当与这颗树的根,区间[1,10]的根设为1,所以[1,5]的就为1<<1=2,[6,10]的根为:(1<<1)+1=3,依次类推,然后用sum[root]表示根节点为root的所代表的这个区间的值,如:sum[1]保存的就是[1,10]这个区间的值。所以有:sum[1]=sum[2]+sum[3],即:sum[root]=sum[root>>1]+sum[(root>>1)+1]

线段树的修改:比方说我要修改某一个结点的值,那么我只需要沿着根结点1往下寻找,并记录这条路径,更新sum的值即可,比方说变化2:它需要修改的区间依次为:[1,10]

->[1,5]->[1,3]->[1,2]->[2,2]。具体操作见代码:

void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val {   if(L==R)    {   sum[root]+=val;        return ;    }     int mid=(L+R)>>1;    if(q<=mid) Update(q,val,Lson); //说明p在左结点     else Update(q,val,Rson); //说明p在右结点     Pushup(root);}

至于统计某一区间[a,b]的值,那么同样可以利用二分的思想:

比方说要求[4,8],二分mid=(4+8)/2=6,然后它就可以写成:[4,6]+[7,8]=[4,5]+[6,6]+[7,7]+[8,8]=[4,4]+[5,5]+...+[8,8]。代码见下题中的Query部分。

HDU 1166(敌兵布阵),相当于树状数组中的插点问线,初始化给你每个结点的值,然后更新每个点的值,求给定的任意一个区间的值。

#include<iostream>#include<cstring>#include<cstdio> using namespace std;const int MAX=50010;#define Lson L,mid,root<<1 //遇到Lson的时候强制替换为后面的语句 #define Rson mid+1,R,root<<1|1int n,sum[MAX<<2];int Pushup(int root) //把当前结点的信息更新到父节点 {   sum[root]=sum[root<<1]+sum[root<<1|1];}void Build(int L,int R,int root){   if(L==R)     {   scanf("%d",&sum[root]);        return ;    }    int mid=(L+R)>>1;    Build(Lson); //左孩子     Build(Rson); //右孩子     Pushup(root); }void Update(int q,int val,int L,int R,int root) //在根为root,区间为[L,R]中的线段树修改结点p的值增加val {   if(L==R)    {   sum[root]+=val;        return ;    }     int mid=(L+R)>>1;    if(q<=mid) Update(q,val,Lson); //说明p在左结点     else Update(q,val,Rson); //说明p在右结点     Pushup(root);}int Query(int ql,int qr,int L,int R,int root) //在根为root,区间为[L,R]的线段树中,计算区间[ql,qr]的和  {   if(ql<=L && R<=qr) return sum[root];    int mid=(L+R)>>1; //利用二分的思想     int res=0;    if(ql<=mid) res+=Query(ql,qr,Lson);    if(qr>mid) res+=Query(ql,qr,Rson);    return res;}int main(){   int a,b,Case,num=1;    scanf("%d",&Case);    while(Case--)    {   printf("Case %d:\n",num++);        scanf("%d",&n);        Build(1,n,1);           char op[10];        while(scanf("%s",op))        {   if(op[0]=='E') break;            scanf("%d%d",&a,&b);            if(op[0]=='A') Update(a,b,1,n,1);            if(op[0]=='S') Update(a,-b,1,n,1);            if(op[0]=='Q') printf("%d\n",Query(a,b,1,n,1));        }    }     return 0;}

 

 

 

 

 

 

 

原创粉丝点击