树状数组的简介和线段树入门

树状数组(用于每次只修改一个值的区间查询)

参考这个,还有这个,图是盗的。。。

这里A[] 是原数组,C[] 就是树状数组了。

由演变过程来看,这里运用了二分的思想。设节点编号为x,那么这个节点所管理的区间长度为 2^k (k为x二进制末尾0的个数)。所以 Cn = A(n - 2^k + 1) + ... + An

感觉结合图跟模板不难懂

int n, m, a[MAXN], c[MAXN << 1];
//计算区间长度 2 ^ k
int Lowbit(int x)
{
    return x & (-x);
}
//修改
void change(int x, int num)
{
    while(x <= n)
    {
        c[x] += num;
        x += Lowbit(x);
    }
}
//查询
int sum(int x)
{
    int ans = 0;
    while(x > 0)
    {
        ans += c[x];
        x -= Lowbit(x);
    }
}

线段树

这篇详解

上图就是线段树的样子,给个简单的解释,例如这棵树是求区间和的话,那个 [1, 10]算的就是 [1, 10]的和

刚好是hdu1166

关于线段树的基本操作有建树,修改,查询

//建树,rt代表根,这里不懂的话手动结合上图模拟,从根开始
void Build(int l, int r, int rt)
{
    if(l == r)
    {
        scanf("%d", &sum[rt]);
        return ;
    }
    int m = (l + r) >> 1;
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    //更新sum
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
//修改,设原数组为 a ,即为 a[x] += num
void Update(int x, int num, int l, int r, int rt)
{
    if(l == r)
    {
        sum[rt] += num;
        return ;
    }
    int m = (l + r) >> 1;
    //看 x 在左区间还是右区间
    if(x <= m)
        Update(x, num, l, m, rt << 1);
    else
        Update(x, num, m + 1, r, rt << 1 | 1);
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
//查询
int Query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R)
        return sum[rt];
    int m = (l + r) >> 1;
    int ans = 0;
    if(L <= m)
        ans += Query(L, R, l, m, rt << 1);//左子区间与[L, R]有重叠
    if(R > m)
        ans += Query(L, R, m + 1, r, rt << 1 | 1);
    return ans;
}

上题题解

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <sstream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <iomanip>
#include <stack>

using namespace std;

typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 50005;
const int MOD = 1e9 + 7;

#define MemI(x) memset(x, -1, sizeof(x))
#define Mem0(x) memset(x, 0, sizeof(x))
#define MemM(x) memset(x, 0x3f, sizeof(x))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
int sum[MAXN << 2];
//建树,rt代表根,这里不懂的话手动结合上图模拟,从根开始
void Build(int l, int r, int rt)
{
    if(l == r)
    {
        scanf("%d", &sum[rt]);
        return ;
    }
    int m = (l + r) >> 1;
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    //更新sum
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
//修改,设原数组为 a ,即为 a[x] += num
void Update(int x, int num, int l, int r, int rt)
{
    if(l == r)
    {
        sum[rt] += num;
        return ;
    }
    int m = (l + r) >> 1;
    //看 x 在左区间还是右区间
    if(x <= m)
        Update(x, num, l, m, rt << 1);
    else
        Update(x, num, m + 1, r, rt << 1 | 1);
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
//查询
int Query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R)
        return sum[rt];
    int m = (l + r) >> 1;
    int ans = 0;
    if(L <= m)
        ans += Query(L, R, l, m, rt << 1);//左子区间与[L, R]有重叠
    if(R > m)
        ans += Query(L, R, m + 1, r, rt << 1 | 1);
    return ans;
}

int main()
{
    int T;
    scanf("%d", &T);
    for(int cas = 1;cas <= T;++cas)
    {
        printf("Case %d:\n", cas);
        int n;
        scanf("%d", &n);
        Build(1, n, 1);
        char s[15];
        while(1)
        {
            scanf("%s", s);
            int x, y;
            if(s[0] == 'A')
            {
                scanf("%d%d", &x, &y);
                Update(x, y, 1, n, 1);
            }
            else if(s[0] == 'S')
            {
                scanf("%d%d", &x, &y);
                Update(x, -y, 1, n, 1);
            }
                else if(s[0] == 'Q')
            {
                scanf("%d%d", &x, &y);
                printf("%d\n", Query(x, y, 1, n, 1));
            }
            else
                break;
        }
    }
    return 0;
}
View Code

后续待更。。。

猜你喜欢

转载自www.cnblogs.com/shuizhidao/p/9581188.html