高级数据结构:线段树


#写在前面

线段树里一般有俩个操作

  1. push_up 由子节点算父节点的信息
  2. push_down 把父节点的修改信息下传到子节点

在这里插入图片描述
在这里插入图片描述
设倒数第二层有n个点,则除去最后一层有 2n-1 个点,加上最后一层的最坏情况(2n个点)一共有4n-1个点,

对于长度为n的数组,一共有n个叶节点,其倒数第二层的点数必少于 n 个,不妨直接将倒数第二层的点设为n个
直接开4n的数组
还会有一些预留的空间

#最大数

https://www.acwing.com/problem/content/1277/

----c++版

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

const int N=200010;
int m,p;
struct Node{
    int l,r;
    int v;// 区间最大值 如果是叶节点则为x号叶节点存的值
}tr[N*4];

void pushup(int u){//由子算u
    tr[u].v=max(tr[u<<1].v, tr[u<<1|1].v);
}

void build(int u, int l, int r){
    tr[u]={l, r};
    if(l==r)return;
    int mid=l+r>>1;
    build(u<<1, l, mid); build(u<<1|1, mid+1, r);
}

int query(int u, int l, int r){
    if(tr[u].l>=l && tr[u].r<= r) return tr[u].v;
    
    int mid=tr[u].l+tr[u].r>>1;
    int v=0;
    if(l<=mid)v=query(u<<1, l, r);
    if(r>mid)v=max(v, query(u<<1|1, l, r));
    
    return v;
}

void modify(int u, int x, int v){
    if(tr[u].l==x&&tr[u].r==x)tr[u].v=v;
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(x<=mid)modify(u<<1, x, v);
        else modify(u<<1|1, x, v);
        pushup(u); //非叶节点才会pushup
    }
}

int main(){
    int n=0, last=0;
    scanf("%d%d", &m, &p);
    build(1, 1, m);
    
    int x;
    char op[2];
    while(m--){
        scanf("%s%d", op, &x);
        if(*op=='Q'){
            last=query(1, n-x+1, n);
            printf("%d\n", last);
        }else{
            modify(1, n+1, (last+x)%p);
            n++;
        }
    }
    
    return 0;
}

#你能回答这些问题吗

https://www.acwing.com/activity/content/problem/content/1608/1/

在这里插入图片描述
在这里插入图片描述

----c++版

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=500010;

int n,m;
int w[N];
struct Node{
    int l,r;
    int sum, lmax, rmax, tmax;
}tr[N*4];

void pushup(Node &u, Node &l, Node &r){
    u.sum=l.sum+r.sum;
    u.lmax=max(l.lmax, l.sum+r.lmax);
    u.rmax=max(r.rmax, r.sum+l.rmax);
    u.tmax=max(max(l.tmax, r.tmax), l.rmax+r.lmax);
}

void pushup(int u){
    pushup(tr[u], tr[u<<1], tr[u<<1|1]);
}

void build(int u, int l, int r){
    if(l==r)tr[u]={l, r, w[r], w[r], w[r], w[r]};
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1, l, mid); build(u<<1|1, mid+1, r);
        pushup(u);
    }
}

void modify(int u, int x, int v){
    if(tr[u].l==x&&tr[u].r==x)tr[u]={x,x,v,v,v,v};
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(x<=mid)modify(u<<1,x,v);
        else modify(u<<1|1 ,x, v);
        pushup(u);
    }
}

Node query(int u, int l, int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u];
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(r<=mid)return query(u<<1, l, r);//全在左子区间
        else if(l>mid)return query(u<<1|1, l, r);//全在右子区间
        else{
            auto left=query(u<<1, l, r);
            auto right=query(u<<1|1, l, r);
            Node res; //这个l, r的区间是新合并出的答案区间
            pushup(res, left, right);
            return res;
        }
    }
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) scanf("%d", &w[i]);
    build(1,1,n);
    
    int k,x,y;
    while(m--){
        scanf("%d%d%d", &k, &x, &y);
        if(k==1){
            if(x>y)swap(x,y);
            printf("%d\n", query(1,x,y).tmax);
        }
        else modify(1,x,y);
    }
    
    return 0;
}

#区间的最大公约数

https://www.acwing.com/problem/content/247/

在这里插入图片描述

----c++版

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;

typedef long long LL;

const int N=500010;

int n,m;
LL w[N];
struct Node{
    int l,r;
    LL sum, d;
}tr[N*4];

LL gcd(LL a, LL b){
    return b? gcd(b, a%b):a;
}

void pushup(Node &u, Node &l, Node &r){
    u.sum=l.sum+r.sum;
    u.d=gcd(l.d, r.d);
}

void pushup(int u){
    pushup(tr[u], tr[u<<1], tr[u<<1|1]);
}

void build(int u, int l, int r){
    if(l==r){
        LL b=w[r]-w[r-1];
        tr[u]={l,r,b,b};
    }else{
        tr[u].l=l, tr[u].r=r;
        int mid=l+r>>1;
        build(u<<1, l, mid), build(u<<1|1, mid+1, r);
        pushup(u);
    }
}

void modify(int u, int x, LL v){
    if(tr[u].l==x&&tr[u].r==x){
        LL b=tr[u].sum+v;
        tr[u]={x,x,b,b};
    }else{
        int mid=tr[u].l+tr[u].r>>1;
        if(x<=mid)modify(u<<1, x, v);
        else modify(u<<1|1, x, v);
        pushup(u);
    }
}

Node query(int u, int l, int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u];
    else{
        int mid=tr[u].l+tr[u].r>>1;
        if(r<=mid) return query(u<<1, l, r);
        else if(l>mid)return query(u<<1|1, l, r);
        else{
            auto left=query(u<<1, l, r);
            auto right=query(u<<1|1, l, r);
            Node res;
            pushup(res, left, right);
            return res;
        }
    }
}

int main(){
    scanf("%d%d", &n,&m);
    for(int i=1; i<=n; i++)scanf("%lld", &w[i]);
    build(1, 1, n);
    
    int l, r;
    LL d;
    char op[2];
    while(m--){
        scanf("%s%d%d", op, &l, &r);
        if(*op=='Q'){
            auto left=query(1,1,l);
            Node right({0,0,0,0});
            if(l+1<=r)right=query(1, l+1, r);// 不能直接用w[l],可能会被修改
            printf("%lld\n", abs(gcd(left.sum, right.d)));//差分可能会有负数
        }
        else{
            scanf("%lld", &d);
            modify(1, l, d);
            if(r+1<=n)modify(1, r+1, -d);
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值