*点击

树状数组的基本操作

树状数组的基本操作

基本操作1:

求比a小在a前面数数量和比a小在a后面数数量:

在看之前,你必须了解树状数组的基本函数;

 

inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void insert(ll x,ll y)//加入 
{
    while(x<=n)
    {
        sum[x]+=y;
        x+=lowbit(x);
    }
}
inline ll findout(ll x)//查找 
{
    ll ans=0;
    while(x)
    {
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}

 

 

求有多少在a前面的数比a小

首先,假如求有多少在a前面的数比a小;

举例:                  1 4 2 3 5

然后有5个空位置, _ _ _ _ _ 为sum[5]

第一步     求出sum[1]前缀,答案是0;

                插入1,  1 _ _ _ _

第二步     求出sum[4]前缀,答案是1;

                插入4,  1 _ _ 4 _

第二步     求出sum[2]前缀,答案是1;

                插入4,  1 2 _ 4 _

..........................

这样不断进行下去sum[i]就是 有多少在i前面的数比a小;

所以就转化成了求前缀和的题目了,自然就想到树状数组了;

但是输入的几个数可能会非常大;

我们只需知道每个数的大小关系,并不需要知道具体值,所以在处理之前可以离散化;

    //离散化 
    for(ll i=1;i<=n;i++)
    {
        a[i].v=read();
        a[i].num=i;//将每一个数的位置记下 
    }
    sort(a+1,a+n+1,cmp);//从小到大排序,
                        //这样每个数都有顺序了,并且每个数对应的位置没有改变 
    for(ll i=1;i<=n;i++)
        b[a[i].num]=i;//把第i小的数位置上赋值为i 

具体怎么实现,读者自行手动模拟;

按上面查找的思路

    for(ll i=1;i<=n;i++)
    {
        ll x=findout(b[i]);
        ans[i]=x;//先求值,再插入,不然会把自己也算进去的 
        insert(b[i],1);
    }

那么求比a小在a后面数数量

则反之

    for(ll i=n;i>=1;i--)
    {
        ll x=findout(b[i]);
        ans[i]=x;//反之 
        insert(b[i],1);
    }

这样就ok了

 

基本操作2:

求1-x的最大值

那么求1-x的最大值,就需要在findout函数中改一改就好了;

我们从基本操作1中代码可以看到;

ans+=sum[x];

是将每个sum[x]的权值相加(具体自己看上面,这里不细讲);

求1-x的最大值,只需将代码改成:

ans=max(ans,sum[x]);

insert函数中就需把 sum[x]+=y;改成 sum[x]=max(sum[x],y);(这个比较容易理解吧)

那么代码就是:

inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void insert(ll x,ll y)//加入 
{
    while(x<=mx)
    {
        sum[x]=max(sum[x],y);//
        x+=lowbit(x);
    }
}
inline ll findout(ll x)//查找 
{
    ll ans=0;
    while(x)
    {
        ans=max(ans,sum[x]);//
        x-=lowbit(x);
    }
    return ans;
}

 

基本操作3:

查询第k大的数的大小

如果数据范围较大,也是需要先进行离散化的

思路是用二分+树状数组;

insert(x,y);
//数x出现了y次,插入树状数组
ll l=1,r=n;
while(l<=r)
{
    ll mid=(l+r)>>1;
    if(findout(mid)>=k)
        r=mid-1;
    else
        l=mid+1;
}
//最后 a[l] 即是第k大的数

 

该操作同样也可以使用线段树和平衡树,

若每次查询的$k$ 有一定规律,或逐渐变化,该操作也可以用 堆 来实现

posted @ 2020-06-25 22:41  木偶人-怪咖  阅读(277)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录