210929算法日记:树状数组

  • 今天刚刚开学提高课(什么,你怎么知道我的基础课还有好多不会的)
  • 咳咳,实在是因为只刷题太枯燥了,所以想着刷累了看会儿课,这几天老是听别人讲树状数组和线段树,发现自己也可以学,虽然我很菜,所以下面就对树状数组做了一个小结
  • 树状数组是干什么的,简而言之就是平均复杂度的前缀和
  • 前缀和应该都知道吧,树状数组能在o(logn)的复杂度内求到前缀和,但是换回的是查询更慢(也需要o(logn))前缀和的查询只需要o1
  • 但众所周知,logn几乎是常数,所以这波树状数组完胜
  • 首先你得维护一个tree数组,他代表的含义就是树状数组里面的值
  • 每一个tree[x]代表[x - lowbit(x) + 1,x]的前缀和,干说有些难懂
  • 像下面这样
x123456789
x的二进制1101110010111011110001001
lowbit(x)121412181
tree[x]a1a1+a2a3a1+…+a4a5a5+a6a7a1+…+a8a9
  • 如何求前缀和
  • sum[8] = tree[8]
  • sum[7] = tree[7]+tree[6]+tree[4]
  • sum[9] = tree[9]+tree[8]
  • 首先从7开始先加上tree[7]
  • 之后7-lowbit[7]=6加上tree[6]
  • 之后6-lowbit[6]=4加上tree[4]
  • 最后4-lowbit(4)=0结束
  • tree数组的更新是同样的
  • 二进制解释比如说x=10000,则x-1=01111
  • 第一段就是(01110~01111],a[15]
  • 第二段就是(01100~01110],a[13] ~ a[14]
  • 第三段就是(01000~01100],a[9] ~ a[12]
  • 第四段就是(00000~01000],a[1] ~ a[8]
  • 加起来再加a[16]就是tree[16]
  • 查询操作: 观察发现tree[x]=a[x]+tree[x-1]+tree[x-lowbit(x)-1]+…
  • 所以对应的循环遍历是
int res=0;
for(int i=x;i;i-=lowbit(i)) res+=tree[i];
return res;
  • 修改操作: 再有就是修改了,我们发现000011100…直接影响到的点有且只有一个00010000…也就是x+lowbit(x)
  • 所以对应的循环遍历是(如果a[x]要加c)
for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=c;
  • 实现很简单,应用也简单,下面用一个例题来终结这篇博客
  • link<-----楼兰图腾
#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+10;
int n,a[N],low[N],great[N];
typedef long long ll;
int tree[N];
int lowbit(int x)
{
    return x&-x;
}
void add(int x)
{
    for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=1;
}
int sum(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tree[i];
    return res;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        int y=a[i];
        low[i]=sum(y-1);
        great[i]=sum(n)-sum(y);
        add(y);
    }
    memset(tree,0,sizeof tree);
    ll ans1=0,ans2=0;
    for(int i=n;i>=1;i--)
    {
        int y=a[i];
        ans1+=1ll*low[i]*(sum(y-1));
        ans2+=1ll*great[i]*(sum(n)-sum(y));
        add(y);
    }
    cout<<ans2<<' '<<ans1<<endl;
    return 0;
}
  • 210823完结
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值