• 算法竞赛模板 线段树


    线段树基础操作

    #include<bits/stdc++.h>
    #define MAX 200000
    using namespace std;
    int a,b,ans,n,m,x,val,op;
    
    struct node
    {
        int l,r,w,f;
    }tree[4*MAX+5];
    
    //建树 左孩子结点为2*k 右孩子为2k+1
    void build(int l,int r,int k)
    {
        tree[k].l=l;
        tree[k].r=r;
        if(tree[k].l==tree[k].r)//叶子结点
        {
            scanf("%d",&tree[k].w);return;
        }
        int mid=(l+r)/2;
        build(l,mid,k*2);    //左孩子
        build(mid+1,r,k*2+1);//右孩子
    
        tree[k].w=tree[k*2].w+tree[k*2+1].w;//状态合并,w为两个孩子w之和
    }
    
    //标记下传
    //f 每个子节点要加的值
    void down(int k)
    {
        tree[k*2].f+=tree[k].f;
        tree[k*2+1].f+=tree[k].f;
    
        tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
        tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
        tree[k].f=0;
    }
    
    //单点查询 待查询点位置为x
    void askp(int k)
    {
        if(tree[k].l==tree[k].r)//当前结点的左右端点相等,即是叶子节点(最终答案)
        {
            ans=tree[k].w;
            return;
        }
        if(tree[k].f)down(k);
        
        int mid=(tree[k].l+tree[k].r)/2;
        if(x<=mid)askp(k*2);//目标位置比中点靠左,则递归左孩子
        else askp(k*2+1);   //靠右,则递归右孩子
    }
    
    //单点修改 比如:对第x个数加上val
    //revise 修改
    void rep(int k)
    {
        if(tree[k].l==tree[k].r)//找到目标位置
        {
            tree[k].w+=val;
            return;
        }
        if(tree[k].f)down(k);
        
        int mid=(tree[k].l+tree[k].r)/2;
        if(x<=mid)rep(k*2);
        else rep(k*2+1);
        
        tree[k].w=tree[k*2].w+tree[k*2+1].w;//所有包含结点k的结点状态更新
    }
    
    //区间查询 区间[a,b]之和
    //a<=mid,即查询区间[a,b]有部分在当前区间的左子区间,递归左孩子
    //b>mid ,即查询区间[a,b]有部分在当前区间的右子区间,递归右孩子
    void askin(int k)
    {
        if(tree[k].l>=a&&tree[k].r<=b)
        {
            ans+=tree[k].w;
            return;
        }
        if(tree[k].f)down(k);
    
        int mid=(tree[k].l+tree[k].r)/2;
        if(a<=mid)askin(k*2);
        if(mid<b)askin(k*2+1);
    }
    
    //区间修改,即修改一段连续区间的值
    void rein(int k)
    {
        if(tree[k].l>=a&&tree[k].r<=b)
        {
            tree[k].w+=(tree[k].r-tree[k].l+1)*val;
            tree[k].f+=val;
            return;
        }
        if(tree[k].f)down(k);
    
        int mid=(tree[k].l+tree[k].r)/2;
        if(a<=mid)rein(k*2);
        if(b>mid)rein(k*2+1);
    
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
    }
    int main()
    {
        scanf("%d%d",&n,&m);    //n个节点,m种操作
        build(1,n,1);           //建树 
        for(int i=1;i<=m;i++)
        {
            ans=0;
            getchar();
            op=getchar();if(op==1)     //单点查询,输出第x个数的值 
            {
                scanf("%d",&x);
                askp(1);
                printf("%d
    ",ans);
            }
            else if(op==2)//单点修改,给第x个数+val  
            {
                scanf("%d%d",&x,&val);
                rep(1);
            }
            else if(op==3)//区间查询[a,b]
            {
                scanf("%d%d",&a,&b);
                askin(1);
                printf("%d
    ",ans);
            }
            else          //区间修改[a,b]
            {
                scanf("%d%d%d",&a,&b,&val);
                rein(1);
            }
        }
        return 0;
    }
  • 相关阅读:
    JavaScript入门二
    JavaScript入门
    CSS样式之补充
    CSS样式之操作属性二
    隔空手势操作
    项目管理培训(2)
    uoot启动过程
    new work
    库函数开发步骤 (转)
    keil(持续更新)
  • 原文地址:https://www.cnblogs.com/kannyi/p/9792671.html
Copyright © 2020-2023  润新知