zoukankan      html  css  js  c++  java
  • Libre OJ 130、131、132 (树状数组 单点修改、区间查询 -> 区间修改,单点查询 -> 区间修改,区间查询)

     这三题均可以用树状数组、分块或线段树来做

    #130. 树状数组 1 :单点修改,区间查询

    题目链接:https://loj.ac/problem/130

    题目描述

    这是一道模板题。

    给定数列 a[1], a[2], dots, a[n]a[1],a[2],,a[n],你需要依次进行 qq 个操作,操作有两类:

    • 1 i x:给定 i,xi,x,将 a[i]a[i] 加上 xx;
    • 2 l r:给定 l,rl,r,求 sum_{i=l}^ra[i]i=lra[i] 的值(换言之,求 a[l]+a[l+1]+dots+a[r]a[l]+a[l+1]++a[r] 的值)

    输入格式

    第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1le n,qle 10^61n,q106。
    第二行 nn 个整数 a[1], a[2], dots, a[n]a[1],a[2],,a[n],表示初始数列。保证 |a[i]|le 10^6a[i]106。
    接下来 qq 行,每行一个操作,为以下两种之一:

    • 1 i x:给定 i,xi,x,将 a[i]a[i] 加上 xx;
    • 2 l r:给定 l,rl,r,求 sum_{i=l}^ra[i]i=lra[i] 的值。

    保证 1le lle rle n,1lrn|x|le 10^6x106。

    输出格式

    对于每个 2 l r 操作输出一行,每行有一个整数,表示所求的结果。

    样例

    样例输入

    3 2
    1 2 3
    1 2 0
    2 1 3

    样例输出

    6

    数据范围与提示

    对于所有数据,1le n,qle 10^6,1n,q106|a[i]|le 10^6a[i]106, 1le lle rle n,1lrn|x|le 10^6x106。

    树状数组解法:

    思路:板子

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6;
    int n,m;
    ll sum[maxn];
    int lowbit(int x){return x&(-x);}
    void add(int x,ll val){
        while(x<=maxn){
            sum[x]+=val;
            x+=lowbit(x);
        }
    }
    ll getsum(int x){
        ll res=0;
        while(x){
            res+=sum[x];
            x-=lowbit(x);
        }
        return res;
    }
    int main(){
        scanf("%d%d",&n,&m);
        ll c;
        for(int i=1;i<=n;i++){
            scanf("%lld",&c);
            add(i,c);
        }
        while(m--){
            int id,x,y;
            scanf("%d%d%d",&id,&x,&y);
            if(id==1)add(x,y);
            else
                cout<<getsum(y)-getsum(x-1)<<endl;
        }
        return 0;
    }

     分块解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    ll a[maxn],sum[maxn];
    int n,q,num,block,l[maxn],r[maxn],belong[maxn];
    void build(){
        block=sqrt(n);
        num=n/block;
        if(n%block)num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;
        for(int i=1;i<=n;i++)
            belong[i]=(i-1)/block+1;
    
        for(int i=1;i<=num;i++)
            for(int j=l[i];j<=r[i];j++)
                sum[i]+=a[j];
    }
    ll query(int x,int y){
        ll res=0;
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++)
                res+=a[i];
            return res;
        }
        for(int i=x;i<=r[belong[x]];i++) res+=a[i];
        for(int i=belong[x]+1;i<belong[y];i++)res+=sum[i];
        for(int i=l[belong[y]];i<=y;i++)res+=a[i];
        return res;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build();
        while(q--){
            int id,x,y;
            scanf("%d%d%d",&id,&x,&y);
            if(id==1){
                a[x]+=y;
                sum[belong[x]]+=y;
            }
            else
                printf("%lld
    ",query(x,y));
        }
        return 0;
    }
    View Code

    线段树解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    int n,q;
    ll tree[maxn*4],a[maxn];
    void pushup(int rt){
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    void build(int l,int r,int rt){
        if(l==r){
            tree[rt]=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        pushup(rt);
    }
    void update(int pos,ll val,int l,int r,int rt){
        if(l==r){
            tree[rt]+=val;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) update(pos,val,l,mid,rt<<1);
        else update(pos,val,mid+1,r,rt<<1|1);
        pushup(rt);
    }
    ll ask(int L,int R,int l,int r,int rt){
        ll res=0;
        if(l>=L&&r<=R) return tree[rt];
        pushup(rt);
        int mid=(l+r)>>1;
        if(L<=mid)res+=ask(L,R,l,mid,rt<<1);
        if(R>mid)res+=ask(L,R,mid+1,r,rt<<1|1);
        return res;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build(1,n,1);
        while(q--){
            int id,x,y;
            scanf("%d%d%d",&id,&x,&y);
            if(id==1) update(x,y,1,n,1);
            else printf("%lld
    ",ask(x,y,1,n,1));
        }
        return 0;
    }
    View Code

    #131. 树状数组 2 :区间修改,单点查询

    题目链接:https://loj.ac/problem/131

    题目描述

    这是一道模板题。

    给定数列 a[1], a[2], dots, a[n]a[1],a[2],,a[n],你需要依次进行 qq 个操作,操作有两类:

    • 1 l r x:给定 l,r,xl,r,x,对于所有 iin[l,r]i[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l], a[l+1], dots, a[r]a[l],a[l+1],,a[r] 分别加上 xx);
    • 2 i:给定 ii,求 a[i]a[i] 的值。

    输入格式

    第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1le n,qle 10^61n,q106。
    第二行 nn 个整数 a[1], a[2], dots, a[n]a[1],a[2],,a[n],表示初始数列。保证 |a[i]|le 10^6a[i]106。
    接下来 qq 行,每行一个操作,为以下两种之一:

    • 1 l r x:对于所有 iin[l,r]i[l,r],将 a[i]a[i] 加上 xx;
    • 2 i:给定 ii,求 a[i]a[i] 的值。

    保证 1le lle rle n,1lrn|x|le 10^6x106。

    输出格式

    对于每个 2 i 操作,输出一行,每行有一个整数,表示所求的结果。

    样例

    样例输入

    3 2
    1 2 3
    1 1 3 0
    2 2

    样例输出

    2

    数据范围与提示

    对于所有数据,1le n,qle 10^6,1n,q106|a[i]|le 10^6a[i]106, 1le lle rle n,1lrn|x|le 10^6x106。

    树状数组解法:

    思路:这里需要用到差分数组,我们定义sum【i】为第i个数与第i-1个数的差,即sum【i】=a【i】-a【i-1】,这就使得a【i】=sum【1】+sum【2】+……sum【i】,就是sum数组的前缀和了,我们用数组数组维护sum数组的前缀和就好了。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    ll sum[maxn];
    int n,q;
    int lowbit(int x){return x&(-x);}
    void add(int x,int val){
        while(x<=n){
            sum[x]+=val;
            x+=lowbit(x);
        }
    }
    ll ask(int x){
        ll res=0;
        while(x){
            res+=sum[x];
            x-=lowbit(x);
        }
        return res;
    }
    int main(){
        cin>>n>>q;
        ll tmp=0;
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;
            add(i,x-tmp);
            tmp=x;
        }
        while(q--){
            int id,l,r,x;
            cin>>id;
            if(id==1){
                cin>>l>>r>>x;
                add(l,x); add(r+1,-x);
            }
            else{
                cin>>x;
                cout<<ask(x)<<endl;
            }
        }
        return 0;
    }

     分块解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    int n,q,block,num;
    ll a[maxn],lazy[maxn],l[maxn],r[maxn],belong[maxn];
    void build(){
        block=sqrt(n);
        num=n/block;
        if(n%block)num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=block*i;
        for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
    }
    void update(int x,int y,int z){
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++)a[i]+=z;
            return;
        }
        for(int i=x;i<=r[belong[x]];i++)a[i]+=z;
        for(int i=belong[x]+1;i<belong[y];i++)lazy[i]+=z;
        for(int i=l[belong[y]];i<=y;i++)a[i]+=z;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build();
        while(q--){
            int id,x,y,z;
            scanf("%d",&id);
            if(id==1){
                scanf("%d%d%d",&x,&y,&z);
                update(x,y,z);
            }
            else{
                scanf("%d",&x);
                printf("%lld
    ",a[x]+lazy[belong[x]]);
            }
        }
        return 0;
    }
    View Code

    线段树解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    int n,q;
    ll a[maxn],tree[maxn*4],lazy[maxn*4];
    void pushup(int rt){tree[rt]=tree[rt<<1]+tree[rt<<1|1];}
    void pushdown(int l,int r,int rt){
        if(lazy[rt]){
            tree[rt<<1]+=lazy[rt]*l;
            tree[rt<<1|1]+=lazy[rt]*r;
            lazy[rt<<1]+=lazy[rt];
            lazy[rt<<1|1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void build(int l,int r,int rt){
        if(l==r){
            tree[rt]=a[l];
            return;
        }
        int mid=l+r>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        pushup(rt);
    }
    void update(int L,int R,int val,int l,int r,int rt){
        if(l>=L&&r<=R){
            tree[rt]+=val*(r-l+1);
            lazy[rt]+=val;
            return ;
        }
        int mid=l+r>>1;
        pushdown(mid-l+1,r-mid,rt);
        if(mid>=L) update(L,R,val,l,mid,rt<<1);
        if(mid<R) update(L,R,val,mid+1,r,rt<<1|1);
        pushup(rt);
    }
    ll ask(int pos,int l,int r,int rt){
        if(l==pos&&l==r)
            return tree[rt];
        int mid=l+r>>1;
        pushdown(mid-l+1,r-mid,rt);
        ll ans;
        if(pos<=mid) return ask(pos,l,mid,rt<<1);
        else return ask(pos,mid+1,r,rt<<1|1);
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build(1,n,1);
        while(q--){
            int id,x,y,z;
            scanf("%d",&id);
            if(id==1){
                scanf("%d%d%d",&x,&y,&z);
                update(x,y,z,1,n,1);
            }
            else{
                scanf("%d",&x);
                printf("%lld
    ",ask(x,1,n,1));
            }
        }
        return 0;
    }
    View Code

    #132. 树状数组 3 :区间修改,区间查询

    题目链接:https://loj.ac/problem/132

    题目描述

    这是一道模板题。

    给定数列 a[1], a[2], dots, a[n]a[1],a[2],,a[n],你需要依次进行 qq 个操作,操作有两类:

    • 1 l r x:给定 l,r,xl,r,x,对于所有 iin[l,r]i[l,r],将 a[i]a[i] 加上 xx(换言之,将 a[l], a[l+1], dots, a[r]a[l],a[l+1],,a[r] 分别加上 xx);
    • 2 l r:给定 l,rl,r,求 sum_{i=l}^ra[i]i=lra[i] 的值(换言之,求 a[l]+a[l+1]+dots+a[r]a[l]+a[l+1]++a[r] 的值)。

    输入格式

    第一行包含 22 个正整数 n,qn,q,表示数列长度和询问个数。保证 1le n,qle 10^61n,q106。
    第二行 nn 个整数 a[1],a[2],dots,a[n]a[1],a[2],,a[n],表示初始数列。保证 |a[i]|le 10^6a[i]106。
    接下来 qq 行,每行一个操作,为以下两种之一:

    • 1 l r x:对于所有 iin[l,r]i[l,r],将 a[i]a[i] 加上 xx;
    • 2 l r:输出 sum_{i=l}^ra[i]i=lra[i] 的值。

    保证 1le lle rle n,1lrn|x|le 10^6x106。

    输出格式

    对于每个 2 l r 操作,输出一行,每行有一个整数,表示所求的结果。

    样例

    样例输入

    5 10
    2 6 6 1 1
    2 1 4
    1 2 5 10
    2 1 3
    2 2 3
    1 2 2 8
    1 2 3 7
    1 4 4 10
    2 1 2
    1 4 5 6
    2 3 4

    样例输出

    15
    34
    32
    33
    50

    数据范围与提示

    对于所有数据,1le n,qle 10^6,1n,q106|a[i]|le 10^6a[i]106, 1le lle rle n,1lrn|x|le 10^6x106。

    树状数组解法:

    思路:与上一题差不多,我们继续用一个数组sum1【i】存第i个数与第i-1个数的差,即

    sum1【x】=a【i】-a【x-1】,a【x】=sum1【1】+sum1【2】+……sum1【x】

    我们也可以很容易得出:

    a【1】+a【2】+……a【x】=sum1【1】+(sum1【1】+sum1【2】)+……(sum1【1】+sum1【2】+……sum1【x】)

                                                =x*sum1【1】+(x-1)*sum1【2】+……sum1【x】

                                                =x*(sum1【1】+sum1【2】+……sum1【x】)-∑(i=1-x)(i-1)*sum1[i]

    ,所以我们就多建立一个数组sum2用来维护(x-1)sum1【x】就好了。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    ll sum1[maxn],sum2[maxn],a[maxn];
    int n,q;
    int lowbit(int x){return x&(-x);}
    void add(int x,ll val){
        for(int i=x;i<=n;i+=lowbit(i)){
            sum1[i]+=val;
            sum2[i]+=(x-1)*val;
        }
    }
    ll ask(int x){
        ll res=0;
        for(int i=x;i;i-=lowbit(i)){
            res+=x*sum1[i]-sum2[i];
        }
        return res;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            add(i,a[i]-a[i-1]);
        }
        while(q--){
            int id,l,r,x;
            scanf("%d",&id);
            if(id==1){
                scanf("%d%d%d",&l,&r,&x);
                add(l,x); add(r+1,-x);
            }
            else{
                scanf("%d%d",&l,&r);
                printf("%lld
    ",ask(r)-ask(l-1));
            }
        }
        return 0;
    }

     分块解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e6+10;
    int n,q,num,block;
    ll lazy[maxn],sum[maxn],a[maxn],l[maxn],r[maxn],belong[maxn];
    void build(){
        block=sqrt(n);
        num=n/block;
        if(n%block)num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;
        for(int i=1;i<=n;i++)
            belong[i]=(i-1)/block+1;
    
        for(int i=1;i<=num;i++)
            for(int j=l[i];j<=r[i];j++)
                sum[i]+=a[j];
    }
    void update(int x,int y,int val){
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++) a[i]+=val;
            sum[belong[x]]+=(y-x+1)*val;
            return;
        }
        for(int i=x;i<=r[belong[x]];i++)a[i]+=val;
        for(int i=belong[x]+1;i<belong[y];i++){
            sum[i]+=block*val;
            lazy[i]+=val;
        }
        for(int i=l[belong[y]];i<=y;i++)a[i]+=val;
        sum[belong[x]]+=(r[belong[x]]-x+1)*val;
        sum[belong[y]]+=(y-l[belong[y]]+1)*val;
    }
    ll ask(int x,int y){
        ll res=0;
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++)res+=a[i]+lazy[belong[x]];
            return res;
        }
        for(int i=x;i<=r[belong[x]];i++)res+=a[i]+lazy[belong[x]];
        for(int i=belong[x]+1;i<belong[y];i++) res+=sum[i];
        for(int i=l[belong[y]];i<=y;i++)res+=a[i]+lazy[belong[y]];
        return res;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build();
        while(q--){
            int id,x,y,z;
            scanf("%d",&id);
            if(id==1){
                scanf("%d%d%d",&x,&y,&z);
                update(x,y,z);
            }
            else{
                scanf("%d%d",&x,&y);
                printf("%lld
    ",ask(x,y));
            }
        }
        return 0;
    }
    View Code

    线段树解法:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    typedef long long ll;
    const ll maxn=1e6+10;
    int n,q;
    ll a[maxn],tree[maxn*4],lazy[maxn*4];
    void pushup(int rt){tree[rt]=tree[rt<<1]+tree[rt<<1|1];}
    void pushdown(int l,int r,int rt){
        if(lazy[rt]){
            tree[rt<<1]+=l*lazy[rt];
            tree[rt<<1|1]+=r*lazy[rt];
            lazy[rt<<1]+=lazy[rt];
            lazy[rt<<1|1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void build(int l,int r,int rt){
        lazy[rt]=0;
        if(l==r){
            tree[rt]=a[l];
            return;
        }
        int mid=l+r>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(int L,int R,ll val,int l,int r,int rt){
        if(l>=L&&r<=R){
            tree[rt]+=val*(r-l+1);
            lazy[rt]+=val;
            return;
        }
        int mid=l+r>>1;
        pushdown(mid-l+1,r-mid,rt);
        if(mid>=L) update(L,R,val,lson);
        if(mid<R) update(L,R,val,rson);
        pushup(rt);
    }
    ll ask(int L,int R,int l,int r,int rt){
        ll res=0;
        if(l>=L&&r<=R) return tree[rt];
        int mid=l+r>>1;
        pushdown(mid-l+1,r-mid,rt);
        if(mid>=L) res+=ask(L,R,lson);
        if(mid<R) res+=ask(L,R,rson);
        return res;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        build(1,n,1);
        while(q--){
            int id,x,y,z;
            scanf("%d",&id);
            if(id==1){
                scanf("%d%d%d",&x,&y,&z);
                update(x,y,z,1,n,1);
            }
            else{
                scanf("%d%d",&x,&y);
                printf("%lld
    ",ask(x,y,1,n,1));
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    开源 Serverless 里程碑:Knative 1.0 来了
    以一致的体验交付和管理云原生多集群应用
    iLogtail使用入门K8S环境日志采集到SLS
    如何在golang代码里面解析容器镜像
    mac vmware 无法复制粘贴
    使用vi编辑时上下左右方向键被转化为A、B、C、D
    left join 和 left outer join 的区别
    设计模式之 适配器模式
    设计模式之 命令模式
    设计模式学习之 策略模式
  • 原文地址:https://www.cnblogs.com/zjl192628928/p/10527577.html
Copyright © 2011-2022 走看看