zoukankan      html  css  js  c++  java
  • 线段树基础与例题

    目录

    1.建树 

    2.单点的添加(修改)

    3.区间查询

    4.区间修改(带懒惰标记)

    5.区间查询(带懒惰标记)

    例题:

    A.HDU-1166 敌兵布阵

    B.HDU-1754 I Hate It

    C.HDU-1698 Just a Hook

    D.ZOJ-1601 Count the Colors

    E.POJ-3264 Balanced Lineup

    F.HDU-4027 Can you answer these queries? 


    本文章基本只提供模板,建议参考下(https://oi-wiki.org/ds/segment/),里面有详细解释。

    预处理(纯属为了写代码方便,线段树要多次用到这三个,每个代码前面带着这段就ok了):

    #define lson l,m,p<<1     
    #define rson m+1,r,p<<1|1
    void pushplus(int p)
    {
        a[p] = a[p<<1] + a [p<<1|1];  //即a[p]=a[p*2]+a[p*2+1]
    }

    1.建树 :

    void build(int l, int r, int p) {
      // 对 [l,r] 区间建树,当前根的编号为 p
      if (l == r) {
        cin>>a[p];
        return;
      }
      int m = (s + t) >> 1;
      build(lson), build(rson);
      // 递归对左右区间建树
      pushplus(p)

    2.单点的添加(修改):

    void update(int x,int y,int l,int r,int p)  //把x节点增加y(修改为y)
    {
        if(l==r){
            a[p]+=y;   //若是修改这段换成a[p]=y;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m)
            update(x,y,lson);
        else
            update(x,y,rson);
        pushplus(p);
    }

    3.区间查询:

    int query(int x,int y,int l,int r,int p)
    {
        if(x<=l&&r<=y)
            return a[p];
        int m=(l+r)>>1;
        int sum=0;
        if(x<=m)
            sum+=query(x,y,lson);
        if(y>m)
            sum+=query(x,y,rson);
        return sum;
    }

    4.区间修改(带懒惰标记)(下面代码为区间加上某个值,若是区间全部修改为某个值则把代码中+=全改为=即可):

    void update(int l, int r, int c, int s, int t, int p) {
      // [l,r] 为修改区间,c 为被修改的元素的变化量,[s,t] 为当前节点包含的区间,p
      // 为当前节点的编号
      if (l <= s && t <= r) {
        a[p] += (t - s + 1) * c, b[p] += c;
        return;
      }  // 当前区间为修改区间的子集时直接修改当前节点的值,然后打标记,结束修改
      int m = (s + t) / 2;
      if (b[p] && s != t) {
        // 如果当前节点的懒标记非空,则更新当前节点两个子节点的值和懒标记值
        a[p * 2] += b[p] * (m - s + 1), a[p * 2 + 1] += b[p] * (t - m);
        b[p * 2] += b[p], b[p * 2 + 1] += b[p];  // 将标记下传给子节点
        b[p] = 0;                                // 清空当前节点的标记
      }
      if (l <= m) update(l, r, c, s, m, p * 2);
      if (r > m) update(l, r, c, m + 1, t, p * 2 + 1);
      pushplus(p);
    }

    5.区间查询(带懒惰标记)(下面代码为区间修改时加上某个值,若是修改时区间全部修改为某个值则把代码中除了sum后面的+=全改为=即可):

    int getsum(int l, int r, int s, int t, int p) {
      // [l,r] 为修改区间,c 为被修改的元素的变化量,[s,t] 为当前节点包含的区间,p
      // 为当前节点的编号
      if (l <= s && t <= r) return d[p];
      // 当前区间为询问区间的子集时直接返回当前区间的和
      int m = (s + t) / 2;
      if (b[p]) {
        // 如果当前节点的懒标记非空,则更新当前节点两个子节点的值和懒标记值
        a[p * 2] += b[p] * (m - s + 1), a[p * 2 + 1] += b[p] * (t - m),
            b[p * 2] += b[p], b[p * 2 + 1] += b[p];  // 将标记下传给子节点
        b[p] = 0;                                    // 清空当前节点的标记
      }
      int sum = 0;
      if (l <= m) sum += getsum(l, r, s, m, p * 2);
      if (r > m) sum += getsum(l, r, m + 1, t, p * 2 + 1);
      return sum;
    }

    例题:

    A.HDU-1166 敌兵布阵:简单的单点模板题,AC题解:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <ctype.h>
    #include <cstring>
    #include <cstdio>
    #include <sstream>
    #include <cstdlib>
    #include <iomanip>
    #include <string>
    #include <queue>
    #include <map>
    #define M 50005
    #define ll long long
    #define lson l,m,p<<1
    #define rson m+1,r,p<<1|1
    using namespace std;
    const int maxn = 1e6+10;
    const int INF=0xfffffff;
    int a[M<<2];
    
    void pushplus(int rt)
    {
        a[rt] = a[rt<<1] + a [rt<<1|1];
    }
    void buide(int l,int r,int p)
    {
        if(l==r){
            scanf("%d",&a[p]);
            return ;
        }
        int m=(l+r)>>1;
        buide(lson);
        buide(rson);
        pushplus(p);
    }
    void update(int x,int y,int l,int r,int p)
    {
        if(l==r){
            a[p]+=y;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m)
            update(x,y,lson);
        else
            update(x,y,rson);
        pushplus(p);
    }
    int query(int x,int y,int l,int r,int p)
    {
        if(x<=l&&r<=y)
            return a[p];
        int m=(l+r)>>1;
        int sum=0;
        if(x<=m)
            sum+=query(x,y,lson);
        if(y>m)
            sum+=query(x,y,rson);
        return sum;
    }
    int main()
    {
        int t,x,y;
        int T=1;
        cin>>t;
        while(t--){
            printf("Case %d:
    ",T++);
            int n;
            cin>>n;
            buide(1,n,1);
            char str[20];
            while(cin>>str&&str[0]!='E'){
                cin>>x>>y;
                if(str[0]=='A')
                    update(x,y,1,n,1);
                else if(str[0]=='S')
                    update(x,-y,1,n,1);
                else if(str[0]=='Q'){
                    cout<<query(x,y,1,n,1)<<endl;
                }
            }
        }
        return 0;
    }

    B.HDU-1754 I Hate It:只需更换下父节点与子节点的关系即(a[p] = max(a[p<<1] , a [p<<1|1]),AC题解:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <ctype.h>
    #include <cstring>
    #include <cstdio>
    #include <sstream>
    #include <cstdlib>
    #include <iomanip>
    #include <string>
    #include <queue>
    #include <map>
    #define M 50005
    #define ll long long
    #define lson l,m,p<<1
    #define rson m+1,r,p<<1|1
    using namespace std;
    const int maxn = 1e6+10;
    const int INF=0xfffffff;
    const int maxx=200005;
    int a[maxx*4];
    void pushplus(int rt)
    {
        a[rt] = max(a[rt<<1] , a [rt<<1|1]);
    }
    void build(int l,int r,int p)
    {
        if(l==r)
        {
            scanf("%d",&a[p]);
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,p*2);
        build(mid+1,r,p*2+1);
        pushplus(p);
    }
    void update(int x,int y,int l,int r,int p)
    {
        if(l==r){
            a[p]=y;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m)
            update(x,y,lson);
        else
            update(x,y,rson);
        pushplus(p);
    }
    int query(int x,int y,int l,int r,int p)
    {
        if(x<=l&&r<=y)
            return a[p];
        int m=(l+r)>>1;
        int sum=0;
        if(x<=m)
            sum=max(query(x,y,lson),sum);
        if(y>m)
            sum=max(query(x,y,rson),sum);
        return sum;
    }
    int main()
    {
        int n,t,x,y;
        while(scanf("%d%d",&n,&t)!=EOF){
            build(1,n,1);
            while(t--){
               char str;
               scanf(" %c %d %d",&str,&x,&y);
               if(str=='U')
                   update(x,y,1,n,1);
               else
                    printf("%d
    ",query(x,y,1,n,1));
            }
        }
        return 0;
    }

    C.HDU-1698 Just a Hook:区间修改的模板题,只需注意数据类型即可,AC题解:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <ctype.h>
    #include <cstring>
    #include <cstdio>
    #include <sstream>
    #include <cstdlib>
    #include <iomanip>
    #include <string>
    #include <queue>
    #include <map>
    #define M 50005
    #define LL long long
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    const long long maxn = 2e5+5;
    const int INF=0xfffffff;
    const int maxx=200005;
    long long sum[maxx<<2],ad[maxx<<2];
    void pushup(long long rt)
    {
        sum[rt] = sum[rt<<1] + sum [rt<<1|1];
    }
    void build(long long l,long long r,long long rt)
    {
        ad[rt]=0;
        if(l==r)
        {
            sum[rt]=1;
            return ;
        }
        long long m=(l+r)>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(long long L,long long R,long long c,long long l,long long r,long long rt)
    {
        if(L<=l&&R>=r){
            ad[rt]=c;
            sum[rt]=(r-l+1)*c;
            return ;
        }
        long long m=(l+r)>>1;
        if(ad[rt]){
            sum[rt*2]=ad[rt]*(m-l+1);  sum[rt*2+1]=ad[rt]*(r-m);
            ad[rt*2]=ad[rt]; ad[rt*2+1]=ad[rt];
            ad[rt]=0;
        }
        if(L<=m)
            update(L,R,c,lson);
        if(R>m)
            update(L,R,c,rson);
        pushup(rt);
    }
    int main()
    {
        int kase=0;
        long long q,n,t,c,x,y;
        cin>>t;
        while(t--){
            cin>>n>>q;
            build(1,n,1);
            while(q--){
                scanf("%lld %lld %lld",&x,&y,&c);
                update(x,y,c,1,n,1);
            }
            printf("Case %d: The total value of the hook is %d.
    ",++kase , sum[1]);
        }
        return 0;
    }

    D.ZOJ-1601 Count the Colors:区间染色问题,AC题解:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <ctype.h>
    #include <cstring>
    #include <cstdio>
    #include <sstream>
    #include <cstdlib>
    #include <iomanip>
    #include <string>
    #include <queue>
    #include <map>
    #define M 50005
    #define LL long long
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    using namespace std;
    const long long maxn = 2e5+5;
    const int INF=0xfffffff;
    const int maxx=200005;
    long long num[maxx<<2],ad[maxx<<2];
    int nad;
    void update(long long L,long long R,long long c,long long l,long long r,long long rt)
    {
        if(L<=l&&R>=r){
            ad[rt]=c;
            return ;
        }
        long long m=(l+r)>>1;
        if(ad[rt]!=-1){
            ad[rt*2]=ad[rt]; ad[rt*2+1]=ad[rt];
            ad[rt]=-1;
        }
        if(L<=m)
            update(L,R,c,lson);
        if(R>m)
            update(L,R,c,rson);
    }
    void query(long long l,long long r,long long rt)
    {
        if(l==r){
            if(ad[rt]>=0&&ad[rt]!=nad)
                num[ad[rt]]++;
            nad=ad[rt];
            return ;
        }
        long long m=(l+r)>>1;
        if(ad[rt]!=-1){
            ad[rt*2]=ad[rt]; ad[rt*2+1]=ad[rt];
            ad[rt]=-1;
        }
            query(lson);
            query(rson);
    }
    int main()
    {
        long long n,x,y,c;
        while(scanf("%lld",&n)!=-1){
            nad=-1;
            memset(num,0,sizeof(num));
            memset(ad,-1,sizeof(ad));
            for(int i=1;i<=n;i++){
                scanf("%lld%lld%lld",&x,&y,&c);
                if(x<y)
                update(x+1,y,c,1,8000,1);
            }
            query(1,8000,1);
            for(int i=0;i<=8000;i++){
                if(num[i]) printf("%d %lld
    ",i,num[i]);
            }
            printf("
    ");
    
        }
        return 0;
    }

    E.POJ-3264 Balanced Lineup:基础模板题,AC题解:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <ctype.h>
    #include <cstring>
    #include <cstdio>
    #include <sstream>
    #include <cstdlib>
    #include <iomanip>
    #include <string>
    #include <queue>
    #include <map>
    #define M 50005
    #define ll long long
    #define lson l,m,p<<1
    #define rson m+1,r,p<<1|1
    using namespace std;
    const int maxn = 1e6+10;
    const int INF=0xfffffff;
    const int maxx=200005;
    int a[maxx*4],b[maxx*4];
    void pushplus(int rt)
    {
        a[rt] = max(a[rt<<1] , a [rt<<1|1]);
    }
    void pushplus1(int rt)
    {
        b[rt] = min(b[rt<<1] , b [rt<<1|1]);
    }
    void build(int l,int r,int p)
    {
        if(l==r)
        {
            scanf("%d",&a[p]);
            b[p]=a[p];
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,p*2);
        build(mid+1,r,p*2+1);
        pushplus(p);
        pushplus1(p);
    }
    int query(int x,int y,int l,int r,int p)
    {
        if(x<=l&&r<=y)
            return a[p];
        int m=(l+r)>>1;
        int sum=0;
        if(x<=m)
            sum=max(query(x,y,lson),sum);
        if(y>m)
            sum=max(query(x,y,rson),sum);
        return sum;
    }
    int query1(int x,int y,int l,int r,int p)
    {
        if(x<=l&&r<=y)
            return b[p];
        int m=(l+r)>>1;
        int sum=INF;
        if(x<=m)
            sum=min(query1(x,y,lson),sum);
        if(y>m)
            sum=min(query1(x,y,rson),sum);
        return sum;
    }
    int main()
    {
        int n,m,x,y;
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--){
               scanf("%d %d",&x,&y);
               printf("%d
    ",query(x,y,1,n,1)-query1(x,y,1,n,1));
        }
        return 0;
    }

    F.HDU-4027 Can you answer these queries? :本题需要一点优化技巧。在进行更新操作的时候,由于是开根号取整,1开根号是其本身,所以在每个子区间进行更新时,特判一下会快很多(这个优化非常重要),避免超时。AC题解:

    #include<bits/stdc++.h>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    const int maxn=1e5+5;
    typedef long long ll;
    ll sum[maxn<<2];
    void PushUP(int rt)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
     
    void build(int l,int r,int rt)
    {
        if(l==r)
        {
            scanf("%lld",&sum[rt]);
            return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        PushUP(rt);
    }
    void update(int L,int R,int l,int r,int rt)
    {
        if(l==r)
        {
            sum[rt]=sqrt(sum[rt]);
            return ;
        }
        if(L<=l&&R>=r&&sum[rt]==r-l+1)//如果区间内的所有数都是1则不必更新
            return;
        int m=(l+r)>>1;
        if(L<=m)
            update(L,R,lson);
        if(m<R)
            update(L,R,rson);
        PushUP(rt);
    }
    ll query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            return sum[rt];
        }
        int m=(l+r)>>1;
        ll ret=0;
        if(L<=m)
            ret+=query(L,R,lson);
        if(R>m)
            ret+=query(L,R,rson);
        return ret;
    }
    int main()
    {
        int n,m,t=0;
        while(scanf("%d",&n)==1)
        {
            int a,b,c;
            build(1,n,1);
            scanf("%d",&m);
            printf("Case #%d:
    ",++t);
            while(m--)
            {
                scanf("%d%d%d",&a,&b,&c);
                if(b>c) swap(b,c);
                if(a)
                    printf("%lld
    ",query(b,c,1,n,1));
                else
                    update(b,c,1,n,1);
            }
            printf("
    ");
        }
        return 0;
    }
  • 相关阅读:
    安卓使用spinner控件和pull解析实现全国省市县的三级联动(附上xml文件)
    安卓linearlayout布局的一个嵌套实例
    接口回调的例子和安卓中的接口回掉实例
    Android Studio 快捷键
    java比较器 之compareable 和comparato比较
    4.Gradle构建Spring Boot项目
    2.Gradle安装和常用命令
    1.Gradle基础介绍
    6.SpringBoot学习(六)——Spring Boot Banner自定义
    4.SpringBoot学习(四)——Spring Boot Validation校验及原理
  • 原文地址:https://www.cnblogs.com/shmilky/p/14089057.html
Copyright © 2011-2022 走看看