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

那是一个春光明媚的下午

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

邪恶的老师

学懂了才能走!

老师你看我核善的微笑
22

然后于痛苦和绝望中

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

然后,

一道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:\n",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\n",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ϵ{num1,…numn}
                  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个区间
编号为 20 + 21 + 22 + ··· +2q - 2 + a
现在…
这个区间的左儿子的编号          这个区间的编号×2
⇔(20 + 21 + 22 + ··· +2q - 2 + a)* 2 = 20 + 21 + 22 + ··· +2q - 1 + (a - 1) * 2 + 1
⇔ 21 + 22 + 23 + ··· +2q - 1 + 2 * a = 1 + 21 + 22 + ··· +2q - 1 + (a - 1) * 2 + 1
⇔ 2 + 22 + 23 + ··· +2q - 1 + 2 * a = 2 + 21 + 22 + ··· +2q - 1 + 2 * a - 2
⇔ 22 + 23 + ··· +2q - 1 + 2 * a = 22 + ··· +2q - 1 + 2 * a
⇔ 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\n",ans);
        }
    }
    return 0;
}

然后这道题就可以搞搞了

还有这道

以及这道

猜你喜欢

转载自blog.csdn.net/qq_42421714/article/details/85058716
今日推荐