树状数组(3)

树状数组的一些基本题型吧
之前说过,树状数组能做的东西线段树都能做
代码这么简单的树状数组能不能做线段树经常要做的改点(段)求段(点)呢?
废话,这么简单的东西答案当然是能啦

1、

改点求段

codevs 1080

代码:

#include <cstdio>
#include <cstring>
using namespace std;
int c[100010],l[100010],n;
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int y)
{
    while (x<=100000)
    {
        c[x]+=y;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int s=0;
    while (x>=1)
    {
        s+=c[x];
        x-=lowbit(x);
    }
    return s;
}
int main()
{
    int n,x,y,z,u,m;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&u);
        add(i,u);
    }
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&u);
        if (u==1) 
        {
            scanf("%d%d",&x,&z);
            add(x,z);
        }
        else 
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",getsum(y)-getsum(x-1));
            //[x~y]=[1~Y]-[1~X-1];
        }
    }
    return 0;
}

2、

改段求点

codevs1081

#include <cstdio>
#include <cstring>
using namespace std;
int c[100010],l[100010],n;
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int y)
{
    while (x<=100000)
    {
        c[x]+=y;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int s=0;
    while (x>=1)
    {
        s+=c[x];
        x-=lowbit(x);
    }
    return s;
}
int main()
{
    int n,x,y,z,u,m;
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&u);
        add(i,u);//[i~n]都加z
        add(i+1,-u);//只更改i这个点,所以[i+1,n]以后的点要还原
    }
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&u);
        if (u==1) 
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x,z);//[x~n]的点都加z
            add(y+1,-z);//因为只是更改[x~y]的点,所以[y+1~n]要还原
        }
        else 
        {
            scanf("%d",&x);
            printf("%d\n",getsum(x));
        }
    }
    return 0;
}

3、

改段求段

codevs 1082

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200009
using namespace std;
int n,m;long long c[2][N];
int lowbit(int x) 
{
    return x&-x;
}
void add(int x,long long y,int opp)
{
    while(x<=n)
    {
        c[opp][x]+=y;
        x+=lowbit(x);
    }
}
long long getsum(int x,int opp)
{
    long long s=0;
    while(x>=1)
    {
        s+=c[opp][x];
        x-=lowbit(x);
    }
    return s;
}
long long ans(int x)//ASK:ans=X.sum(k)*k+Y.sum(k)
{
    return (x+1)*getsum(x,0)+getsum(x,1);
}
int main()
{
    int i,j,k,x,y,opp;
    long long z;
    scanf("%d",&n);
    for (i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x,0),add(i+1,-x,0);
        add(i,-x*i,1),add(i+1,x*(i+1),1);
    }
    int m;scanf("%d",&m); 
    while(m--)
    {
        scanf("%d%d%d",&opp,&x,&y);
        if(opp==1)
        {
            scanf("%lld",&z);
            /*
            CHANGE:
            X[L]+=val,X[R+1]-=val
            Y[L]+=-val*(L-1),Y[R+1]+=val*R;
            */
            add(x,z,0),add(y+1,-z,0);
            add(x,-x*z,1),add(y+1,(y+1)*z,1);
        }
        else printf("%lld\n",ans(y)-ans(x-1));
    }

}
### 树状数组数据结构实现及应用 #### 什么是树状数组树状数组(Binary Indexed Tree, BIT)是一种基于数的二进制特性的高效数据结构,用于支持动态数组上的区间查询和单点/区间更新操作。它通过一种特殊的树形结构来存储累积信息,从而能够在对数时间内完成这些操作[^1]。 #### 树状数组的核心特性 树状数组的主要特点在于其编程简单性和高效的性能表现。相比于其他复杂的数据结构(如线段树),树状数组仅需少量代码即可实现核心功能,并且运行速度更快[^3]。 #### 基本操作原理 树状数组的操作依赖于 `lowbit` 技术,该技术能够提取整数最低位的1及其后续零所表示的数值。具体而言: - **Lowbit函数定义**:对于任意正整数 \( x \),\( lowbit(x) = x \& (-x) \)[^4]。 此函数帮助定位父节点以及子节点之间的关系,在构建和维护树状数组时起到关键作用。 #### 查询与更新方法 以下是树状数组两种基本操作的具体实现: ##### 单点更新 当需要改变原始数组中的某一项值时,必须同步调整树状数组对应位置及其祖先结点的信息。算法如下所示: ```cpp void update(int idx, int delta){ while(idx <= n){ t[idx] += delta; idx += lowbit(idx); } } ``` ##### 区间求和 为了计算从索引1到指定索引处所有元素之和,可以采用累加方式逐步访问相关联的节点直到根部为止。伪代码形式呈现如下: ```cpp int query(int idx){ int res = 0; while(idx > 0){ res += t[idx]; idx -= lowbit(idx); } return res; } ``` 上述两部分构成了完整的树状数组框架,适用于处理频繁发生的范围汇总请求与局部修正指令组合场景下的优化解决方案[^2]^。 #### 应用实例分析 假设存在一个长度为n的一维数组a[],目标是对这个序列执行多次交替进行的修改命令(增加或者减少特定下标的数值大小)同时间歇性询问关于不同片段内的总合状况,则运用BIT将是理想的选择之一因为每次交互都能维持O(logN)的时间消耗级别. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值