• 笔记—树状数组&&线段树


    那是一个春光明媚的下午

    我们被锁在机房里,被逼学树状数组

    邪恶的老师

    学懂了才能走!

    老师你看我核善的微笑

    然后于痛苦和绝望中

    我们学会了树状数组&&线段树(只是一点很肤浅的)

    然后,

    一道ban题

    现在谈谈树状数组

    学过的都知道

    也很轻松就能推出

    单点修改-区间查询

    区间修改-单点查询

    区间修改-区间查询,我就推不出来了orz(qwq

    先说说

    单点修改-区间查询

    而树状数组代码大概长这样

    恰好有板题一道 ⇒敌兵布阵
    还有一道⇒更ban的题

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
     
    const int M = 50001;
     
    int n,tot,Bit[M];
     
    inline int lowbit(int x)
    {
        return x & -x;
    }
     
    inline void update(int Index,int delta)
    {
        int i;
        for(i  = Index;i <= n;i += lowbit(i))
            Bit[i] += delta;
    }
     
    inline int sum(const int Index)
    {
        int i,Sum = 0;
        for(i = Index;i >0;i -= lowbit(i))
            Sum += Bit[i];
        return Sum;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            tot++;
            printf("Case %d:
    ",tot);
            int i;
            scanf("%d",&n);
            for(i = 1;i <= n;i++)
            {
                int soldier;
                scanf("%d",&soldier);
                update(i,soldier);
            }
            char s[15];
            while(1)
            {
                scanf("%s",s);
                if(s[0] == 'E')
                    break;
                int u,v;
                scanf("%d %d",&u,&v);
                if(s[0] == 'A')
                    update(u,v);
                if(s[0] == 'S')
                    update(u,-v);
                if(s[0] == 'Q')
                    printf("%d
    ",sum(v) - sum(u - 1));
            }
            memset(Bit,0,sizeof(Bit));
        }
        return 0;
    }
    
    

    再谈谈

    区间修改-单点查询

    ban题

    其实
    它和单点修改-区间查询很像

    就这样搞搞

    一个序列

    ↓                ↓

    1 2 3 4 5 6 7 8 9

    要使1 - 7区间内加上5

    update(1,5);
    update(8,-5);
    

    应对查询就

    sum(Index);
    

    ok

    ⇝ ⇝ ⇝ ⇝就有经验

    区间修改-区间查询

    蒟蒻的我

    再也推不出来了

    看下表

    6:00

    (⊙o⊙)…
    看来是走不了了

    不行,我的fate/stay night[unlimited blade works]还没看完
    我的saber
    我的手办
    祈的歌我还没有听够

    我怎么会就这样陨落
    这时,一声神秘的voice出现
    少年,学线段树吧

    于是就有了...

    首先建树

    inline void tree_built(int l,int r,int k)
    {
        if(l == r)
        {
            tree[k] = num[l];
            return;
        }
        int mid = l + r >> 1;
        k = k << 1;
        //<<左移一位(2进制),位运算,优先级低于=-*/
        //>>同理
        tree_built(l,mid,k);
        tree_built(mid + 1,r,k ^ 1);
        tree[k >> 1] = tree[k] + tree[k ^ 1];
        //因为此时k的二进制末位一定为0,0 ^ 1 = 0 + 1
    }
    

    设有xϵ{ (num_1),...(num_n)}

                      1 ~ 8

                     ↙     ↘

                1 ~ 4    5 ~ 8

               ↙ ↘           ↙ ↘

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

    1        2   3   4     5  6  7      8

    可见每次修改一个区间o(log n),
    查询亦为o(log n)
    那为什么

    编号为k的左子树编号为k << 1 
    右子树编号为k << 1 ^ 1  ??
    k = k << 1;
    tree_built(l,mid,k);
    tree_built(mid + 1,r,k ^ 1);
    

    证明:
    第q(q >= 2,前两层手推)层的从左往右第a个区间
    编号为

    (2_0) + (2_1) + (2_2) + ··· +(2_{q - 2}) + a

    现在...

    这个区间的左儿子的编号

              

    这个区间的编号×2

    ⇔ ((2_0) + (2_1) + (2_2) + ··· +(2_{q - 2}) + a)* 2 = (2_0) + (2_1) + (2_2) + ··· +(2_{q - 1}) + (a - 1) * 2 + 1

    ⇔ 2 * a = 2 * a

    得证.

    然后时间复杂度大约为o(树的结点数)

    然后求和

    int query(int l,int r,int k)
    {
        if(r <= Right&&l >= Left)
            return tree[k];
        int mid = l + r >> 1;
        int k = k << 1;
        int sum = 0;
        if(Left <= mid)
            sum = modify(l,mid,k);
        if(Right > mid)
            sum += modify(mid + 1,r,k ^ 1);
        return sum;
    }
    

    清晰易懂,不用解释

    修改

    inline void modify(int k,int l,int r,int delta)
    {
        if(Left <= l&&Right >= r)
        {
            Add(k,l,r,delta);
            return;
        }
        int mid = l + r >> 1;
        PushDown(k,l,r,mid);
        //如果,要往下,先将这里的修改值传下去
        if(Left <= mid)
            modify(k << 1,l,mid,delta);
        if(mid < Right)
            modify(k << 1 ^ 1,mid + 1,r,delta);
        tree[k] = tree[k << 1] + tree[k << 1 ^ 1];
    }
    

    上面就有两个函数
    Add&PushDown

    这里用的是标记下传的方法

    inline void Add(int k,int l,int r,int delta)
    {
        add[k] += delta;
        tree[k] += ll(delta) * (r - l + 1);
    }
    
    inline void PushDown(int k,int l,int r,int mid)
    {
        if(add[k] == 0)
            return;
        Add(k << 1,l,mid,add[k]);
        Add(k << 1 ^ 1,mid + 1,r,add[k]);
        add[k] = 0;
    }
    

    求和

    inline ll query(int k,int l,int r)
    {
        if(r <= Right&&l >= Left)
            return tree[k];
        int mid = l + r >> 1;
        PushDown(k,l,r,mid);
        //唯一值得注意的就是这里也需要下传
        //因为上一次可能修改了1~n,只有tree[1]被修改了,而查询的却是1~1
        ll ans = 0;
        if(mid >= Left)
            ans = query(k << 1,l,mid);
        if(Right > mid)
            ans += query(k << 1 ^ 1,mid + 1,r);
        return ans;
    }
    

    合起来

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 100000;
    ll tree[MAXN * 4];
    int num[MAXN + 1],add[MAXN * 4],Left,Right;
    inline void Read(int *x)
    {
        *x = 0;
        char a = getchar();
        bool f = 0;
        while(a > '9'||a < '0') {if(a == '-') f = 1;a = getchar();}
        while(a <= '9'&&a >= '0') {*x = *x * 10 + a - '0';a = getchar();}
        if(f)
            *x *= -1;
    }
    inline void tree_built(int l,int r,int k)
    {
        if(l == r)
        {
            tree[k] = num[l];
            return;
        }
        int mid = l + r >> 1;
        k = k << 1;
        tree_built(l,mid,k);
        tree_built(mid + 1,r,k ^ 1);
        tree[k >> 1] = tree[k] + tree[k ^ 1];
    }
    inline void Add(int k,int l,int r,int delta)
    {
        add[k] += delta;
        tree[k] += ll(delta) * (r - l + 1);
    }
    inline void PushDown(int k,int l,int r,int mid)
    {
        if(add[k] == 0)
            return;
        Add(k << 1,l,mid,add[k]);
        Add(k << 1 ^ 1,mid + 1,r,add[k]);
        add[k] = 0;
    }
    inline ll query(int k,int l,int r)
    {
        if(r <= Right&&l >= Left)
            return tree[k];
        int mid = l + r >> 1;
        PushDown(k,l,r,mid);
        ll ans = 0;
        if(mid >= Left)
            ans = query(k << 1,l,mid);
        if(Right > mid)
            ans += query(k << 1 ^ 1,mid + 1,r);
        return ans;
    }
    inline void modify(int k,int l,int r,int delta)
    {
        if(Left <= l&&Right >= r)
        {
            Add(k,l,r,delta);
            return;
        }
        int mid = l + r >> 1;
        PushDown(k,l,r,mid);
        if(Left <= mid)
            modify(k << 1,l,mid,delta);
        if(mid < Right)
            modify(k << 1 ^ 1,mid + 1,r,delta);
        tree[k] = tree[k << 1] + tree[k << 1 ^ 1];
    }
    int main()
    {
    	int i,n,m;
    	Read(&n),Read(&m);
    	for(i = 1;i <= n;i++)
            scanf("%d",&num[i]);
    	tree_built(1,n,1);
    	while(m--)
        {
            int Case;
            Read(&Case),Read(&Left),Read(&Right);
            if(Case == 1)
            {
                int delta;
                Read(&delta);
                modify(1,1,n,delta);
            } else {
                ll ans = query(1,1,n);
                printf("%lld
    ",ans);
            }
        }
        return 0;
    }
    
    

    然后这道题就可以搞搞了

    还有这道

    那么,难一点的

    这道

    #include <cstdio>
    #include <cstring>
    const int MAXN = 100001;
    int n,m,p,num[MAXN],Left,Right;
    long long add[MAXN * 4],add2[MAXN * 4],tree[MAXN * 4];
    inline void tree_build(int l,int r,int k)
    {
        add2[k] = 1;
        if(l == r)
        {
            tree[k] = num[l];
            return;
        }
        int mid = l + r >> 1;
        tree_build(l,mid,k << 1);
        tree_build(mid + 1,r,k << 1 ^ 1);
        tree[k] = (tree[k << 1] + tree[k << 1 ^ 1]) % p;
    }
    inline void Add(int l,int r,int k,long long delta,bool Case)
    {
        if(!Case)
        {
            tree[k] = (tree[k] + (r - l + 1) * (delta % p)) % p;
            add[k] = (delta % p + add[k]) % p;
        } else {
            tree[k] = tree[k] % p * delta % p;
            add2[k] = add2[k] * delta % p;
            add[k] = add[k] * delta % p;
        }
    }
    inline void PushDown(int l,int r,int k)
    {
        int mid = l + r >> 1;
        Add(l,mid,k << 1,add2[k],1);
        Add(l,mid,k << 1,add[k],0);
        Add(mid + 1,r,k << 1 ^ 1,add2[k],1);
        Add(mid + 1,r,k << 1 ^ 1,add[k],0);
        add2[k] = 1;
        add[k] = 0;
    }
    inline long long query(int l,int r,int k)
    {
        if(Left <= l&&r <= Right)
            return tree[k];
        int mid = l + r >> 1;
        long long ans = 0;
        PushDown(l,r,k);
        if(Left <= mid)
            ans = query(l,mid,k << 1);
        if(mid < Right)
            ans += query(mid + 1,r,k << 1 ^ 1);
        return ans % p;
    }
    inline void modify(int l,int r,int k,long long delta,bool Case)
    {
        if(Left <= l&&r <= Right)
        {
            Add(l,r,k,delta,Case);
            return;
        }
        PushDown(l,r,k);
        int mid = l + r >> 1;
        if(Left <= mid)
            modify(l,mid,k << 1,delta,Case);
        if(mid < Right)
            modify(mid + 1,r,k << 1 ^ 1,delta,Case);
        tree[k] = (tree[k << 1] + tree[k << 1 ^ 1]) % p;
    }
    int main()
    {
        int i;
        scanf("%d%d%d",&n,&m,&p);
        for(i = 1;i <= n;i++)
            scanf("%lld",&num[i]);
        tree_build(1,n,1);
        for(i = 1;i <= m;i++)
        {
            int Case;
            long long delta;
            scanf("%d%d%d",&Case,&Left,&Right);
            if(Case == 3)
                printf("%lld
    ",query(1,n,1));
            else {
                scanf("%lld",&delta);
                if(Case == 2) modify(1,n,1,delta,0);
                else modify(1,n,1,delta,1);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    Python的网络编程 Socket编程
    Python之数据库模块安装 MySQLdb
    Python的内置函数
    Servlet及Tomcat介绍
    xml解析
    JDBC基础
    反射练习
    lambda和匿名内部类
    Object.wait()实现生产者消费者模式
    synchronized、lock及线程安全集合
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11323286.html
Copyright © 2020-2023  润新知