线段树模板

区间修改/区间查询

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10,inf = 0x3f3f3f3f;
int n,m,a[N],add[N * 4];
ll sum[N * 4];
void build(int k,int l,int r) //建树 
{
    if(l == r)
    {
        sum[k] = a[l];return ;
    }
    int mid = (l + r) / 2;
    build(k * 2,l,mid);
    build(k * 2 + 1,mid + 1,r);
    sum[k] = sum[k * 2] + sum[k * 2 + 1];
}
void Add(int k,int l,int r,int v) //区间加并更新
{
    add[k] += v;
    sum[k] += ll(v) * (r - l + 1); 
} 
void pushdown(int k,int l,int r,int mid) //标记下传 
{
    if(add[k] == 0)return;
    Add(k * 2,l,mid,add[k]);
    Add(k * 2 + 1,mid + 1,r,add[k]);
    add[k] = 0;
}
ll query(int k,int l,int r,int x,int y) //查询x到y区间的和 
{
    if(l >= x && r <= y)return sum[k];
    int mid = (l + r) / 2;
    ll res = 0;
    pushdown(k,l,r,mid);
    if(x <= mid)res += query(k * 2,l,mid,x,y);
    if(mid < y)res += query(k * 2 + 1,mid+1,r,x,y);
    return res; 
}
void modify(int k,int l,int r,int x,int y,int v) //x到y的区间加v 
{
    if(l >= x && r <= y)return Add(k,l,r,v);
    int mid = (l + r) / 2;
    pushdown(k,l,r,mid);
    if(x <= mid)modify(k * 2,l,mid,x,y,v);
    if(mid < y)modify(k * 2 + 1,mid + 1,r,x,y,v);
    sum[k] = sum[k * 2] + sum[k * 2 + 1]; 
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    while(m--)
    {
        char op;int x,y,z;
        while((op=getchar())!='Q'&&op!='C');
        scanf("%d %d",&x,&y);
        if(op=='Q')
        {
            printf("%lld\n",query(1,1,n,x,y));
        }
        else
        {
            scanf("%d",&z);
            modify(1,1,n,x,y,z);
        }
    }
     return 0;
}
View Code

 区间修改/单点查询,只需要修改query函数的传参即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10,inf = 0x3f3f3f3f;
int n,m,a[N],add[N * 4];
ll sum[N * 4];
void build(int k,int l,int r) //建树 
{
    if(l == r)
    {
        sum[k] = a[l];return ;
    }
    int mid = (l + r) / 2;
    build(k * 2,l,mid);
    build(k * 2 + 1,mid + 1,r);
    sum[k] = sum[k * 2] + sum[k * 2 + 1];
}
void Add(int k,int l,int r,int v) //区间加并更新
{
    add[k] += v;
    sum[k] += ll(v) * (r - l + 1); 
} 
void pushdown(int k,int l,int r,int mid) //标记下传 
{
    if(add[k] == 0)return;
    Add(k * 2,l,mid,add[k]);
    Add(k * 2 + 1,mid + 1,r,add[k]);
    add[k] = 0;
}
ll query(int k,int l,int r,int x,int y) //查询x到y区间的和 
{
    if(l >= x && r <= y)return sum[k];
    int mid = (l + r) / 2;
    ll res = 0;
    pushdown(k,l,r,mid);
    if(x <= mid)res += query(k * 2,l,mid,x,y);
    if(mid < y)res += query(k * 2 + 1,mid+1,r,x,y);
    return res; 
}
void modify(int k,int l,int r,int x,int y,int v) //x到y的区间加v 
{
    if(l >= x && r <= y)return Add(k,l,r,v);
    int mid = (l + r) / 2;
    pushdown(k,l,r,mid);
    if(x <= mid)modify(k * 2,l,mid,x,y,v);
    if(mid < y)modify(k * 2 + 1,mid + 1,r,x,y,v);
    sum[k] = sum[k * 2] + sum[k * 2 + 1]; 
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    while(m--)
    {
        char op;int x,y,z;
        while((op=getchar())!='Q'&&op!='C');
        scanf("%d",&x);
        if(op=='Q')
        {
            printf("%lld\n",query(1,1,n,x,x)); //单点查询,所以查询从x到x区间和 
        }
        else
        {
            scanf("%d %d",&y,&z);
            modify(1,1,n,x,y,z);
        }
    }
     return 0;
}
View Code

 区间乘mul/区间加add/区间查询

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10,inf = 0x3f3f3f3f;
int n,m;
int c[N];
ll t[N * 4],add[N * 4],mul[N * 4]; //分别是线段树、加法、乘法 
void build(int k,int l,int r)
{
    add[k] = 0;
    mul[k] = 1; //建树顺便初始化 
    if(l == r)
    {
        t[k] = c[l];
        return;
    }
    int mid = (l + r) / 2;
    build(k * 2,l,mid);
    build(k * 2 + 1,mid + 1,r);
    t[k] = t[k * 2] + t[k * 2 + 1];
}
void pushdown(int k,int l,int r,int mid)
{
    //更新顺序:树、乘、加
    //左子树 = 左子树 * k层的积 + [l-mid]区间和 右子树 =  右子树 * k层的积 + [mid-r]区间和
    t[k * 2] = t[k * 2] * mul[k] + (mid - l + 1) * add[k]; 
    t[k * 2 + 1] = t[k * 2 + 1] * mul[k] + (r - mid) * add[k];
    //乘法 = 左/右儿子 * 父节点mul
    mul[k * 2] = mul[k * 2] * mul[k];
    mul[k* 2 + 1] = mul[k * 2 + 1] * mul[k];  
    //加法 = 左右儿子 * 父节点mul + 父节点add 
    add[k * 2] = add[k * 2] * mul[k] + add[k];
    add[k * 2 + 1] = add[k * 2 + 1] * mul[k] + add[k];
    mul[k] = 1;
    add[k] = 0; //重置父节点 
}
void multiply(int k,int l,int r,int a,int b,int w)
{
    if(l > b || r < a)return;
    if(l >= a && r <= b)
    {    //乘法 : 树、乘、加都要乘w 
        mul[k] *= w;
        add[k] *= w; 
        t[k] *= w;
        return;
    }
    int mid = (l + r) / 2;
    pushdown(k,l,r,mid);
    multiply(k * 2,l,mid,a,b,w);
    multiply(k * 2 + 1,mid + 1,r,a,b,w);
    t[k] = t[k * 2] + t[k * 2 + 1];
}
void modify(int k,int l,int r,int a,int b,int w)
{
    if(l > b || r < a)return;
    if(l >= a && r <= b)
    {
        t[k] += w * (r - l + 1); //k层 的[l-r]区间和都加上w 
        add[k] += w;
        return;
    }
    int mid = (l + r) / 2;
    pushdown(k,l,r,mid);
    modify(k * 2,l,mid,a,b,w);
    modify(k * 2 + 1,mid + 1,r,a,b,w);
    t[k] = t[k * 2] + t[k * 2 + 1];
}
ll query(int k,int l,int r,int a,int b)
{
    if(l > b || r < a)return 0;
    if(l >= a && r <= b)return t[k];
    int mid = (l + r) / 2;
    pushdown(k,l,r,mid);
    ll res = 0;
    res += query(k * 2,l,mid,a,b);
    res += query(k * 2 + 1,mid + 1,r,a,b);
    return res;
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)scanf("%d",&c[i]);
    build(1,1,n);
    int x,y,z;
    char op;
    while(m--)
    {
        getchar();
        scanf("%c",&op);
        if(op == 'M')
        {
            scanf("%d %d %d",&x,&y,&z);
            multiply(1,1,n,x,y,z);//x~y乘z 
        }
        else if(op == 'C')
        {
            scanf("%d %d %d",&x,&y,&z);
            modify(1,1,n,x,y,z);//x~y加z 
        }
        else
        {
            scanf("%d %d",&x,&y);
            printf("%lld\n",query(1,1,n,x,y)); //查询x~y区间和 
        }
    }
     return 0;
}
View Code

 

posted @ 2023-09-07 18:03  CRt0729  阅读(7)  评论(0编辑  收藏  举报