线段树

简直噩梦,几道板子题我都debug好久

单点修改,区间查询

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long int LL;
struct tree{
    LL left,right,sum,num;
}tree[N*4];
LL a[N],ans;
void build(int x,int l,int r){
    tree[x].left=l,tree[x].right=r;
    tree[x].num=0;
    if(l==r){
        tree[x].sum=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    return ;
}
void add(int i,int x,int k){
    if(tree[i].left==x&&tree[i].right==x){
        tree[i].sum+=k;
        return ;
    }
    if(tree[i*2].right>=x)add(i*2,x,k);
    if(tree[i*2+1].left<=x)add(i*2+1,x,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}
void searchs(int i,int l,int r){
    if(tree[i].left>=l&&tree[i].right<=r){
        ans+=tree[i].sum;
        return ;
    }
    if(tree[i*2].right>=l)searchs(i*2,l,r);
    if(tree[i*2+1].left<=r)searchs(i*2+1,l,r);
}
int main(){
    LL n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        int op,x,k,l,r;
        cin>>op;
        if(op==1){
            cin>>x>>k;
            add(1,x,k);
        }
        else {
           cin>>l>>r;
           ans=0;
           searchs(1,l,r);
           cout<<ans<<endl;
        }
    }
}

区间修改,单点查询

#include<bits/stdc++.h>

using namespace std;
const int N=5e5+10;
typedef long long int LL;
struct tree{
    LL left,right,sum,num;
}tree[N*4];
LL a[N],ans;
void build(int x,int l,int r){
    tree[x].left=l,tree[x].right=r;
    tree[x].num=0;
    if(l==r){
        tree[x].sum=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    //cout<<tree[x].sum<<endl;
    return ;
}
void add(int x,int l,int r,int k){
    if(tree[x].left>=l&&tree[x].right<=r){
        tree[x].num+=k;
        return ;
    }
    if(tree[x*2].right>=l)add(x*2,l,r,k);
    if(tree[x*2+1].left<=r)add(x*2+1,l,r,k);
}
void searchs(int x,int p){
    ans+=tree[x].num;
    if(tree[x].left==tree[x].right)return ;
    if(p<=tree[x*2].right)searchs(x*2,p);
    if(p>=tree[x*2+1].left)searchs(x*2+1,p);
}
int main(){
    LL n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        int op,l,r,k;
        cin>>op;
        if(op==1){
            cin>>l>>r>>k;
            add(1,l,r,k);
        }
        else {
            cin>>l;
            ans=0;
            searchs(1,l);
            cout<<ans+a[l]<<endl;
        }
    }
}

区间修改,区间查询

区间加法

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long int LL;
struct tree{
    LL left,right,sum,lz;
}tree[N*4];
LL a[N],ans;
void build(int x,int l,int r){
    tree[x].left=l,tree[x].right=r;
    if(l==r){
        tree[x].sum=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
    return ;
}
void push_down(int i){
    if(tree[i].lz!=0){
        int mid=(tree[i].left+tree[i].right)/2;
        tree[i*2].sum+=(mid-tree[i*2].left+1)*tree[i].lz;
        tree[i*2+1].sum+=(tree[i*2+1].right-mid)*tree[i].lz;
        tree[i*2].lz+=tree[i].lz;
        tree[i*2+1].lz+=tree[i].lz;
        tree[i].lz=0;
    }
}
void add(int i,int l,int r,int k){
    if(tree[i].left>=l&&tree[i].right<=r){
        tree[i].sum+=(tree[i].right-tree[i].left+1)*k;
        tree[i].lz+=k;
        return;
    }
    push_down(i);
    int mid=(l+r)/2;
    if(tree[i*2].right>=l)add(i*2,l,r,k);
    if(tree[i*2+1].left<=r)add(i*2+1,l,r,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}

void searchs(int i,int l,int r){
    if(tree[i].left>=l&&tree[i].right<=r){
        ans+=tree[i].sum;
        return ;
    }
    push_down(i);
    if(tree[i*2].right>=l)searchs(i*2,l,r);
    if(tree[i*2+1].left<=r)searchs(i*2+1,l,r);
}
int main(){
    LL n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        int op;
        cin>>op;
        int l,r,k;
        if(op==1){
            cin>>l>>r>>k;
            add(1,l,r,k);
        }
        else{
            cin>>l>>r;
            ans=0;
            searchs(1,l,r);
            cout<<ans<<endl;
        }
    }
}

区间加法+乘法

//改了好久
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
#define int long long
struct tree{
    int left,right,sum,mulz,addz;
}tree[N*4];
int a[N],ans,p;
void build(int x,int l,int r){
    tree[x].left=l,tree[x].right=r;
    tree[x].mulz=1;
    if(l==r){
        tree[x].sum=a[l]%p;
        return ;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x].sum=(tree[x*2].sum+tree[x*2+1].sum)%p;
    return ;
} 
void push_down(int i){             
    //子线段的加法懒标记=原先的乘以父节点的乘法懒标记+父节点的加法懒标记
    // sum需要在push_down中计算,若在ans那里计算的话,(倘若第一次就进入会被计算重复),push_down 后再计算
    tree[i*2].sum=(tree[i*2].sum*tree[i].mulz+tree[i].addz*(tree[i*2].right-tree[i*2].left+1)%p)%p;//两次模p
    tree[i*2+1].sum=(tree[i*2+1].sum*tree[i].mulz+tree[i].addz*(tree[i*2+1].right-tree[i*2+1].left+1)%p)%p;
    tree[i*2].mulz=tree[i*2].mulz*tree[i].mulz%p;
    tree[i*2+1].mulz=tree[i*2+1].mulz*tree[i].mulz%p;
    tree[i*2].addz=(tree[i*2].addz*tree[i].mulz+tree[i].addz)%p;
    tree[i*2+1].addz=(tree[i*2+1].addz*tree[i].mulz+tree[i].addz)%p;
    tree[i].mulz=1;
    tree[i].addz=0;
    return ;
}
void add(int i,int l,int r,int k){
    if(tree[i].left>=l&&tree[i].right<=r){
        tree[i].sum=(tree[i].sum+(tree[i].right-tree[i].left+1)*k)%p;
        tree[i].addz=(tree[i].addz+k)%p;
        return;
    }
    push_down(i);
    int mid=(l+r)/2;
    if(tree[i*2].right>=l)add(i*2,l,r,k);
    if(tree[i*2+1].left<=r)add(i*2+1,l,r,k);
    tree[i].sum=(tree[i*2].sum+tree[i*2+1].sum)%p;
}
void mul(int i,int l,int r,int k){
    if(tree[i].left>=l&&tree[i].right<=r){
        tree[i].sum=(tree[i].sum*k)%p;
        tree[i].addz=(tree[i].addz*k)%p;
        tree[i].mulz=tree[i].mulz*k%p;
        return ;
    }
    push_down(i);
    if(tree[i*2].right>=l)mul(i*2,l,r,k);
    if(tree[i*2+1].left<=r)mul(i*2+1,l,r,k);
    tree[i].sum=(tree[i*2].sum+tree[i*2+1].sum)%p;
}
void searchs(int i,int l,int r){
    if(tree[i].left>=l&&tree[i].right<=r){
        ans=(ans+tree[i].sum)%p;
        return ;
    }
    push_down(i);
    if(tree[i*2].right>=l)searchs(i*2,l,r);
    if(tree[i*2+1].left<=r)searchs(i*2+1,l,r);
}
signed main(){
    int n,m;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        int op;
        cin>>op;
        int l,r,k;
        if(op==1){
            cin>>l>>r>>k;
            mul(1,l,r,k);
        }
        else if(op==2){
            cin>>l>>r>>k;
            add(1,l,r,k);
        }
        else{
            cin>>l>>r;
            ans=0;
            searchs(1,l,r);
            cout<<ans%p<<endl;
        }
    }
}

 

练习

最大数

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
int a[N],ans;
struct tree{
    int l,r,maxx;
}tree[N*4];
void build(int i,int l,int r){
    tree[i].l=l;tree[i].r=r;
    if(l==r){
        tree[i].maxx=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
    tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx);
    return ;
}

void add(int i,int x,int k){
    if(tree[i].l==tree[i].r&&tree[i].l==x){
        tree[i].maxx=max(tree[i].maxx,k);
        return ;
    }
    if(x<=tree[i*2].r)add(i*2,x,k);
    if(x>=tree[i*2+1].l)add(i*2+1,x,k);
    tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx);
}
int searchs(int i,int l,int r){
    if(tree[i].l>=l&&tree[i].r<=r){
        ans=max(ans,tree[i].maxx);
        return ans;
    }
    if(tree[i*2].r>=l)searchs(i*2,l,r);
    if(tree[i*2+1].l<=r)searchs(i*2+1,l,r);
    return ans;
}
signed main(){
    int m,p;
    cin>>m>>p;
    build(1,1,m);
    int n=0;
    while(m--){
        char ch;int x;
        cin>>ch>>x;
        if(ch=='Q'){
            ans=0;
            cout<<searchs(1,n-x+1,n)<<endl;
        }
        else{
            add(1,n+1,(x+ans)%p);
            n++;
        }
    }
}

 区间最大连续字段和

tmax 存储最大连续字段和,只有tmax转移不过来

rmax最大后缀和,lmax 最大前缀和
用左子节点的最大后缀和和右子节点的最大前缀和以及他们的最大连续字段和向父亲tmax转移
父亲的lmax和rmax需要用sum以及子节点lmax和rmax转移

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
int a[N],ans;
struct Tree{
    int l,r,lmax, rmax, tmax,sum;

}tree[N*4];
void build(int i,int l,int r){
    tree[i].l=l;tree[i].r=r;
    if(l==r){
        tree[i].sum=a[l];
        tree[i].lmax=tree[i].rmax=tree[i].tmax=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(i*2,l,mid);    
    build(i*2+1,mid+1,r);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    tree[i].lmax=max(tree[i*2].lmax,tree[i*2].sum+tree[i*2+1].lmax);
    tree[i].rmax=max(tree[i*2+1].rmax,tree[i*2].rmax+tree[i*2+1].sum);
    tree[i].tmax=max(max(tree[i*2].tmax,tree[i*2+1].tmax),tree[i*2].rmax+tree[i*2+1].lmax);
    return ;
}
void xg(int i,int x,int k){
    if(tree[i].l==tree[i].r&&tree[i].l==x){
        tree[i].sum=k;
        tree[i].lmax=tree[i].rmax=tree[i].tmax=k;
        return ;
    }
    if(x<=tree[i*2].r)xg(i*2,x,k);
    if(x>=tree[i*2+1].l)xg(i*2+1,x,k);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    tree[i].lmax=max(tree[i*2].lmax,tree[i*2].sum+tree[i*2+1].lmax);
    tree[i].rmax=max(tree[i*2+1].rmax,tree[i*2].rmax+tree[i*2+1].sum);
    tree[i].tmax=max(max(tree[i*2].tmax,tree[i*2+1].tmax),tree[i*2].rmax+tree[i*2+1].lmax);
}
Tree searchs(int i,int l,int r){
    if(tree[i].l>=l&&tree[i].r<=r){
        return tree[i];
    }
    int mid=(tree[i].l+tree[i].r)/2;
    if(mid>=r)return searchs(i*2,l,r);   //在当前左半区间
    if(mid<l)return searchs(i*2+1,l,r);  //在当前右半区间
    auto left=searchs(i*2,l,r);           //两边都有
    auto right=searchs(i*2+1,l,r);  
    Tree v;
    v.sum=left.sum+right.sum;
    v.lmax=max(left.lmax,left.sum+right.lmax);
    v.rmax=max(right.rmax,left.rmax+right.sum);
    v.tmax=max(max(left.tmax,right.tmax),left.rmax+right.lmax);//合并答案
    return v;
}
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        int k,x,y;
        cin>>k>>x>>y;
        if(k==1){
            if(x>y)swap(x,y);
            cout<<searchs(1,x,y).tmax<<endl;
        }
        else{
            xg(1,x,y);
        }
    }
}

 区间最大公约数

这是一个区间修改区间查询问题,若用push_down来写gcd没法更新(我不会),当你查询到在向下传递懒标记的话,你还是没法更新,除非push_down到最底层(那还不如多次单点修改)。

然后想到用差分数组来写(感觉和树状数组想法有点像?是不可以用线段树套树状数组来写),我们每个建树时存储差分(这样的话就把区间加d改成单点修改)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+10;
int a[N],ans;
struct Tree{
    int l,r,sum,v;
}tree[N*4];
void build(int i,int l,int r){
    tree[i].l=l;tree[i].r=r;
    if(l==r){
        tree[i].v=a[r]-a[r-1];
        tree[i].sum=a[r]-a[r-1];
        return ;
    }
    int mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
    tree[i].v=__gcd(tree[i*2].v,tree[i*2+1].v);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    return ;
}
void add(int i,int x,int d){
    if(tree[i].l==x&&tree[i].r==x){
        tree[i].v=tree[i].sum+d;
        tree[i].sum+=d;
        return ;
    }
    if(x<=tree[i*2].r)add(i*2,x,d);
    if(x>=tree[i*2+1].l)add(i*2+1,x,d);
    tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    tree[i].v=__gcd(tree[i*2].v,tree[i*2+1].v);
    return ;
}
Tree searchs(int i,int l,int r){
    if(tree[i].l>=l&&tree[i].r<=r){
        return tree[i];
    }
    int mid=(tree[i].l+tree[i].r)/2;
    if(r<=mid)return searchs(i*2,l,r);
    if(l>mid)return searchs(i*2+1,l,r);
    auto left=searchs(i*2,l,r);
    auto right=searchs(i*2+1,l,r);
    Tree u;
    u.sum=left.sum+right.sum;
    u.v=__gcd(left.v,right.v);     //更新答案用的,不向上更新
    return u;
}
signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    while(m--){
        char op;
        int l,r,d;
        cin>>op>>l>>r;
        if(op=='C'){
            cin>>d;
            add(1,l,d);
            if(r+1<=n)add(1,r+1,-d);
        }
        else{
            auto ll=searchs(1,1,l);
            Tree rr={0,0,0,0};
            if(l+1<=r)rr=searchs(1,l+1,r);
            cout<<abs(__gcd(ll.sum,rr.v))<<endl;
        }

    }
}

 

 
posted @ 2022-11-02 21:06  Dengpc  阅读(35)  评论(0)    收藏  举报