算法竞赛模板 线段树

线段树基础操作

#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\n",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\n",ans);
        }
        else          //区间修改[a,b]
        {
            scanf("%d%d%d",&a,&b,&val);
            rein(1);
        }
    }
    return 0;
}

 

posted @ 2018-10-15 18:28  真想不出名字了  阅读(238)  评论(0编辑  收藏  举报