浅谈——线段树

#include<cstdio>
using namespace std;
const int maxn = 100005;
int n,m,x,y,flag;
int l[4*maxn],r[4*maxn];//l[x]:编号为x的单元左端点的数的编号 ,r[x]:编号为x的单元右端点的数的编号  
long long lazy[8*maxn],sum[4*maxn];// sum[x]:编号为x的单元表示的所有数的和(其实只有一个数) 
                                   // lazy[x]:要对编号为x的单元所加的数的多少 
                                
void build(int L,int R,int now)//建一个由单元组成的树 
{
    lazy[now] = 0;//初始化 
    l[now] = L;//开始时第一个单元(根单元)编号为一,左端点的数的编号是1 
    r[now] = R;//右端点的数的编号是数的总个数n 
    if(L == R)//最底层的叶子节点只有一个数,左端点就等于右端点 
    {
        scanf("%lld",&sum[now]);//读入一个数据 
        return;
    }
    int mid = (L+R)/2;//将数的总数分为两半,二分的思想,类似于满二杈树 
    build(L,mid,now*2);//建上一个单元的左侧的子单元 
    build(mid+1,R,now*2+1);//建上一个单元的右侧的子单元 
    sum[now] = sum[now*2] + sum[now*2+1];//求二杈树中两个子单元所对应的父亲单元的值,一层层向上搭建 
}

long long query(int L,int R,int now)//对它进行加数的操作,改变其值 
{
    sum[now] += lazy[now] * (r[now] - l[now] + 1);//改变所求区间的值,弥补缺陷 
    lazy[now*2] += lazy[now];//lazy的值下传到其左侧的子区间 
    lazy[now*2+1] += lazy[now];//lazy的值下传到其右侧的子区间 
    lazy[now] = 0;//lazy的值清零,防止其二次访问产生多次重复操作造成答案错误 
    if(l[now] == L && r[now] == R)return sum[now];//找到所需要的区间,返回其表示的数的和 
    int mid = (l[now]+r[now])/2;//将现在的区间一分为二,即找现在区间的子区间 
    if(R <= mid) return query(L,R,now*2);//如果所求区间的右端点的数的编号小于左侧的子区间的右端点的数的编号,则所求区间被左侧的子区间所覆盖,开始寻找左侧的子区间
    else if(L >= mid+1) return query(L,R,now*2+1);//如果所求区间的左端点的数的编号小于右侧的子区间的左端点的数的编号,则所求区间被右侧的子区间所覆盖,开始寻找右侧的子区间
    else return query(L,mid,now*2)+query(mid+1,R,now*2+1);//如果都不是的话则证明所求区间被分开了,这时应该分头寻找,在左右两个子区间内寻找并求和
}

void modify(int L,int R,long long d,int now)//寻找所求的区间,可能是分开的两个区间 
{
    if(l[now] == L && r[now] == R)//找到所需要的区间
    {
        lazy[now] += d;//标记对所需要的区间加上的数的多少,但所求区间的值未发生改变 
        return;//返回(如果是两个分开的区间,则开始寻找另一个区间) 
    }
    sum[now] += (long long)d*(R-L+1);//d*(R-L+1)所要求加的总值,比如对2~4加2,则总共要加上6),它所有的根区间的值都发生了变化 
    int mid = (l[now]+r[now])/2;//将现在的区间一分为二,即找现在区间的子区间 
    if(R <= mid) modify(L,R,d,now*2);//如果所求区间的右端点的数的编号小于左侧的子区间的右端点的数的编号,则所求区间被左侧的子区间所覆盖,开始寻找左侧的子区间 
    else if(L >= mid+1) modify(L,R,d,now*2+1);//如果所求区间的左端点的数的编号小于右侧的子区间的左端点的数的编号,则所求区间被右侧的子区间所覆盖,开始寻找右侧的子区间
    else//如果都不是的话则证明所求区间被分开了,这时应该分头寻找 
    {
        modify(L,mid,d,now*2);//开始寻找左侧的子区间 
        modify(mid+1,R,d,now*2+1);//开始寻找右侧的子区间
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    build(1,n,1);//建一个由单元组成的树  
    while(m)
    {
        m--;//总操作次数 
        scanf("%d",&flag);//判断要让你干什么,是加数还是求和 
        if(flag == 1)//是加数 
        {
            long long k;
            scanf("%d%d%lld",&x,&y,&k);
            modify(x,y,k,1);//开始加数 
        }
        if(flag == 2)//是求和 
        {
            scanf("%d%d",&x,&y);
            printf("%lld\n",query(x,y,1));//输出求和 
        }
    }
    return 0;//结束程序 
}


我知道你们都很强,应该不需要我讲...

所以...看代码好咯!我的代码很详细的!

posted @ 2018-12-04 20:02  容老衲补一刀  阅读(106)  评论(0编辑  收藏  举报