树状数组基础总结

树状数组基础总结

入门博文:http://www.cnblogs.com/acgoto/p/8583952.html

树状数组与线段树的思想的一些相同与不同。

共同点:同时一个节点维护多个叶子节点的信息

不同点:线段树节点采用二分的规则,而树状数组节点利用了bit位的性质来锁定管理的叶子节点,且树状数组只能直接查询1~i区间的信息,而线段树可以直接查询 [ l , r ] [l,r] [l,r] 区间信息,

树状数组可以解决问题的思路推广到线段树都可以用线段树解决。不过树状数组代码量是真的小!

树状数组核心函数:

这样的代码要求其叶子节点必须从1开始(因为此处维护的都是1~i的信息,可以改下代码使维护0到i的信息,不过代码就不简洁了)

int lowbit(ll val){ //获得val的1的最低位的二进制权值
    return  val&(-val);
}
int getsum(ll k)//遍历1~k区间的管理节点。
{
    ll ans=0;
    while(k>0)
    {
        ans+=bt[k];
        k-=lowbit(k);
    }
    return ans;
}
void modify(ll k,ll val)//向上更新管理叶子节点k的节点信息。
{
    while(k<=n)
    {
        bt[k]+=val;
        k+=lowbit(k);
    }
}

洛谷模板题,改点查段:https://www.luogu.org/problemnew/show/P3374

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+100;
ll  qwe[maxn],n,q;
int lowbit(ll val){
    return  val&(-val);
}
int getsum(ll k)
{
    ll ans=0;
    while(k>0)
    {
        ans+=qwe[k];
        k-=lowbit(k);
    }
    return ans;
}
int modify(ll k,ll val)
{
    while(k<=n)
    {
        qwe[k]+=val;
        k+=lowbit(k);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>q;
    for(int i=1;i<=n;++i){
        ll val;
        cin>>val;
        modify(i,val);
    }
    while(q--)
    {
        ll fa,x,y;
        cin>>fa>>x>>y;
        if(fa==1)
        {
            modify(x,y);
        }
        else{
            cout<<getsum(y)-getsum(x-1)<<endl;
        }

    }
    return 0;

}

比如:LIS(最长递增子序列),求逆序对,改点查段,改段查点的问题都可以用树状数组解决。

经典问题转化:

LIS(最长递增子序列)思想:

f [ i ] f[i] f[i] 表示当前以值 i i i 为递增子序列末尾的序列最长长度。每个叶子 i i i 记录当前 f [ i ] f[i] f[i], 最大值,那么用树状数组节点维护其叶子节点的最大值即可。如果该此时序列中数字值为val,那么 f [ v a l ] = m a x ( f [ i ] ) i ∈ [ 1 , v a l ] + 1 =     g e t m a x ( i ) + 1 f[val]=max(f[i])_{i\in[1,val]}+1 =\ \ ~getmax(i)+1 f[val]=max(f[i])i[1,val]+1=   getmax(i)+1 ps:有时候需要离散化,所以还是二分求LIS比较好用

求逆序对思想:

每个叶子 i i i 记录当前值i的出现次数,那么用树状数组节点维护其管理的叶子节点出现次数和即可,假设当前第k个数其值为val,那么前面大于val的个数= k − 1 − g e t s u m ( v a l ) k-1-getsum(val) k1getsum(val)

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+100;
int tot,n;
int unq[maxn],qwe[maxn],bt[maxn];
int getind(int val)
{
    return lower_bound(unq,unq+tot,val)-unq;
}
int lowbit(int val)
{
    return val&-val;
}
ll getsum(int k)
{
    ll ans=0ll;
    while(k>0)
    {
        ans+=bt[k];
        k-=lowbit(k);
    }
    return ans;
}
void modify(int k)//k叶子+1
{
    while(k<=n)
    {
        ++bt[k];
        k+=lowbit(k);
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=0;i<n;++i)
        cin>>unq[i],qwe[i]=unq[i];
    sort(unq,unq+n);
    tot=unique(unq,unq+n)-unq;
    ll ans=0ll;
    mset(bt,0);
    for(int i=0;i<n;++i)
    {
        int ind=getind(qwe[i])+1;
//        cout<<"ind:"<
        ans+=i-getsum(ind);
        modify(ind);
        /*先求逆序数i- 1~val的个数 然后插入*/
    }
    cout<<ans<<endl;
}

改段查点:

修改:将区间 $[l,r] $ 的每个数加val,查询:求点k处的值

假设原数组为 N u m [ ] Num[] Num[],我们用 d [ ] d[] d[] 记录相邻元素的差值,比如 d [ i ] = n u m [ i ] − n u m [ i − 1 ] d[i]=num[i]-num[i-1] d[i]=num[i]num[i1],且我们让 d [ 1 ] = n u m [ 1 ] d[1]=num[1] d[1]=num[1] ,那么 n u m [ i ] = d [ 1 ] + d [ 2 ] . . . . . + d [ i ] num[i]=d[1]+d[2].....+d[i] num[i]=d[1]+d[2].....+d[i]

至于修改操作,我们只需将 d [ l ] d[l] d[l]加上 v a l val val d [ r + 1 ] d[r+1] d[r+1]减去 v a l val val即可,

我们维护d数组即可。

洛谷模板:https://www.luogu.org/problemnew/show/P3368

代码:

// luogu-judger-enable-o2
#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+100;
ll  qwe[maxn],n,q;
int lowbit(ll val){
    return  val&(-val);
}
int getsum(ll k)
{
    ll ans=0;
    while(k>0)
    {
        ans+=qwe[k];
        k-=lowbit(k);
    }
    return ans;
}
void modify(ll k,ll val)
{
    while(k<=n)
    {
        qwe[k]+=val;
        k+=lowbit(k);
    }
}
ll va[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>q;
    for(int i=1;i<=n;++i){
        cin>>va[i];
    }
    modify(1,va[1]);
    for(int i=2;i<=n;++i)
        modify(i,va[i]-va[i-1]);
    while(q--)
    {
        ll fa,x,y,z;
        cin>>fa;
        if(fa==1)
        {
            cin>>x>>y>>z;
            modify(y+1,-z);
            modify(x,z);
        }
        else{
            cin>>x;
            cout<<getsum(x)<<endl;
        }
    }

    return 0;
}

你可能感兴趣的:(#,树状数组)