求逆序数の树状数组

我们假设一个数组A [n]的,当一个A[N] = 0的时候表示数字N没有在集合中出现过,当一个A[N] = 1时则出现过.A对应的树状数组为C [N]。

  • 树状数组中添加函数:在求逆序数时,要添加数字时(A [1],1),即将该数加一,意思是将数字n加入到集合。

  • 树状数组中总和函数:用于求集合中小于等于数字a的元素个数,所以最后要求比数字a大的元素可以用I-sum([I] -1)(I表示目前集合中的元素个数)

 (被CSDN网站乱码气死

lowbit函数:二进制很强,今后一定要多观察二进制

I&( - I);

这个运算得到的值为K,K就是从我的末尾开始0的个数

k +我得到当前节点的父亲节点的位置

ki得到上一个父亲节点的位置

C [1] = A [1];

C [2] = A [1] + A [2];

C [3] = A [3];

C [4] = A [1] + A [2] + A [3] + A [4];

C [5] = A [5];

C [6] = A [5] + A [6];

C [7] = A [7];

C [8] = A [1] + A [2] + A [3] + A [4] + A [5] + A [6] + A [7] + A [8];

 

转化成二进制

 

 

1 =(001)C [1] = A [1];

2 =(010)C [2] = A [1] + A [2];

3 =(011)C [3] = A [3];

4 =(100)C [4] = A [1] + A [2] + A [3] + A [4];

5 =(101)C [5] = A [5];

6 =(110)C [6] = A [5] + A [6];

7 =(111)C [7] = A [7];

8 =(1000)C [8] = A [1] + A [2] + A [3] + A [4] + A [5] + A [6] + A [7] + A [8];

 

对照式子可以发现   C [1] = A [I-2 ^ K + 1] + A [I-2 ^ K + 2] + ...... A [1]; (K为我的二进制中从最低位到高位连续零的长度)例如I = 8时,K = 3;

区间查询

下面利用C [I]数组,求甲数组中前我项的和 

举个例子i = 7;

sum [7] = A [1] + A [2] + A [3] + A [4] + A [5] + A [6] + A [7]; 前我项和

C [4] = A [1] + A [2] + A [3] + A [4]; C [6] = A [5] + A [6]; C [7] = A [7];

可以推出:sum [7] = C [4] + C [6] + C [7];

序号写为二进制:sum [(111)] = C [(100)] + C [(110)] + C [(111)];

 

再举个例子i = 5

sum [5] = A [1] + A [2] + A [3] + A [4] + A [5]; 前我项和

C [4] = A [1] + A [2] + A [3] + A [4]; C [5] = A [5];

可以推出:sum [5] = C [4] + C [5];

序号写为二进制:sum [(101)] = C [(100)] + C [(101)];

 

细细观察二进制树状数组追其根本就是二进制的应用

结合代码

int getsum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=C[i];
return ans;
}

对于i = 7进行演示 

                                  7(111)                         ans + = C [7]

lowbit(7)= 001 7-lowbit(7)= 6(110)     ans + = C [6]

lowbit(6)= 010 6-lowbit(6)= 4(100)     ans + = C [4]

lowbit(4)= 100 4-lowbit(4)= 0(000)    

对于i = 5进行演示 

                                  5(101)                          ans + = C [5]

lowbit(5)= 001 5-lowbit(5)= 4(100)      ans + = C [4]

lowbit(4)= 100 4-lowbit(4)= 0(000)   

 

 

单点更新

 

当我们修改A []数组中的某一个值时应当如何更新C []数组呢?

回想一下区间查询的过程,再看一下上文中列出的图

 

结合代码分析

void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=y;
}
//可以发现 更新过程是查询过程的逆过程
//由叶子结点向上更新C[]数组

 

当更新A [1]时需要向上更新C [1],C [2],C [4],C [8]

                     C [1],C [2],C [4],C [8]

写为二进制C [(001)],C [(010)],C [(100)],C [(1000)]

                                      1(001)                        C [1] + = A [1]

lowbit(1)= 001 1 + lowbit(1)= 2(010)      C [2] + = A [1]

lowbit(2)= 010 2 + lowbit(2)= 4(100)      C [4] + = A [1]

lowbit(4)= 100 4 + lowbit(4)= 8(1000)    C [8] + = A [1]

 

实现代码:

#include <iostream>
#include <string>
using namespace std;
#define N 1010
int c[N]; 
int n;
int lowbit(int i)
{
    return i&(-i);
}
int insert(int i,int x)
{
    while(i<=n){
        c[i]+=x;
        i+=lowbit(i);
    }
    return 0;
}

int getsum(int i)
{
    int sum=0;
    while(i>0){
        sum+=c[i];
        i-=lowbit(i);
    } 
    return sum;
}
void output()
{
    for(int i=1;i<=n;i++) cout<<c[i]<<" ";
    cout<<endl;
}
int main()
{
    while(cin>>n){
        int ans=0;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++){
            int a;
            cin>>a;
            insert(a,1);
            ans+=i-insert(a);//统计当前序列中大于a的元素的个数
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

 

参考:

https://blog.csdn.net/Small_Orange_glory/article/details/81290634

https://www.cnblogs.com/xiongmao-cpp/p/5043340.html

https://blog.csdn.net/laichilizi/article/details/79450529

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值