树状数组 (及其应用~~~)

菜鸡冒泡~~ 有锅请各位dalao指出

~~ first of all

先交代故事发生的背景.........

关于lowbit的函数介绍(so easy) lowbit是找最后一位是一的位数 ●在计算机中,负数的存储为(以八进制为例,-10 就是10001010※第一位是符号位 对于正数来说,原码,反码,补码都是同一个,对于负数来说,反码就是除去符号位以外,其余的取反 补码是反码最后加一,(加爆了也不用管,加就是了)) ●按位& 例如1&1=1,是两边同时满足才可以 ●一个数和他的相反数用&,则可以求出最后一位是1的位置(就是lowbit的代码实现)(※但要注意的是如果一个数是2的k次方,那么这个数的lowbit还是这个数,手动实现验证) ★关于树状数组,一般是给某一个数加上一个值,还有就是询问某个区间的和, 给某一个数加上一个值,要求更新和这个数有关的所有数,用树状数组可以节省时间大约是O(nlogn) 每次都要update,用c数组去存a数组的值,类似于一个树开始的时候直接加一个值就可以,之后的每次增加某一个数的值要求是每次都要update 而从一个点找到下一个应该被更新的点用的就是lowbit(x)+x; 树状数组中c数组的所代表的数字取决于lowbit的值 例如说lowbit 1=1那么c1就是一个数的和 lowbit 2=2那么c2就是两个数的和,lowbit3=1那么c1就是一个数的和。。。。。。

/*
关于lowbit的函数介绍(so easy)
lowbit是找最后一位是一的位数 
●在计算机中,负数的存储为(以八进制为例,-10 就是10001010※第一位是符号位
对于正数来说,原码,反码,补码都是同一个,对于负数来说,反码就是除去符号位以外,其余的取反
补码是反码最后加一,(加爆了也不用管,加就是了))
●按位&  例如1&1=1,是两边同时满足才可以 
●一个数和他的相反数用&,则可以求出最后一位是1的位置(就是lowbit的代码实现)(※但要注意的是如果一个数是2的k次方,那么这个数的lowbit还是这个数,手动实现验证)
★关于树状数组,一般是给某一个数加上一个值,还有就是询问某个区间的和,
给某一个数加上一个值,要求更新和这个数有关的所有数,用树状数组可以节省时间大约是O(nlogn)
每次都要update,用c数组去存a数组的值,类似于一个树开始的时候直接加一个值就可以,之后的每次增加某一个数的值要求是每次都要update
而从一个点找到下一个应该被更新的点用的就是lowbit(x)+x; 
 树状数组中c数组的所代表的数字取决于lowbit的值
 例如说lowbit 1=1那么c1就是一个数的和  lowbit 2=2那么c2就是两个数的和,lowbit3=1那么c1就是一个数的和。。。。。。 
*/
#include<iostream>
#include<cstdio>
#include<stdio.h>
using namespace std;
int n,m;int c[5000005];
int lowbit(int ii)
{
    return ii&(-ii);
}
void update(int x,int k)
{
    while(x<=n)
    {
        c[x]+=k;
        x=x+lowbit(x);
    }
}
int getsum(int x)
{
    int sum=0;
    while(x!=0)
    {
        sum+=c[x];
        x=x-lowbit(x);
    }
    return sum;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        update(i,a);    
    }
    for(int i=1;i<=m;i++)
    {
        int p,x,y;
        cin>>p>>x>>y;
        if(p==1)
        {
            update(x,y);
        } 
        if(p==2)
        {
            int ckwnb=getsum(y)-getsum(x-1);
            cout<<ckwnb<<endl;

        }
    }   
} 


应用如下

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
using namespace std;
long long int n;long long int c[500015]={0};
struct strr{
  long long int id,wei;
}map[500005];
long long int  com(strr x,strr y)
{
    if(x.wei!=y.wei)
    return x.wei>y.wei;
    if(x.wei==y.wei)
    return x.id>y.id;
}
long long int lowbit(int l)
{
    return l&(-l);
}
void update(int x)
{
    while(x<=n)
    {
        c[x]++;
        x=x+lowbit(x);
    }   
}
long long int getsum(int x)
{
    long long int sum=0;
    while(x>0)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    long long int ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>map[i].wei;
        map[i].id=i;
    }
    sort(map+1,map+n+1,com);
    for(int i=1;i<=n;i++)
    {
        update(map[i].id);
        ans+=getsum(map[i].id-1);
    }
    cout<<ans;
    return 0;
}  
/*make an summary
将一个数的id和这个数的值联系到一起,对值进行排序,但是 实际用到的还是这个数的地址
用树状数组对id排玩顺序以后的数组当做原来的c数组
这是要记得用一个ans变量来记录目前已经有多少是符合要求的了,不断的运用加法即可,
比如说一开始的原数组为5 4 2 6 3 1,按着从大到小拍完顺序以后,可以得到以下的id顺序为4 1 2 5 3 6
此时,把新得到的id作为c数组,不断的加(◆原理和简单)
{
¤第一个数为4
先放进去4 ,这是4是最大的,(要知道,现在加进去的数的id如果很大,(比已经加进去的还要打,就说明这的数的id大但是数目小,这时候看看传输组中
已经加进去的,如果id比这个小但是已经放进去了,就只能说明已经放进去的数比这个id大的数数值大,就符合逆序对的要求,这样就比较明显了
只要在每放一个数的时候,找出id比当前值小的就ok了),于是有了以上代码,◎◎◎◎◎◎◎◎)
last but not least
求逆序对数目的时候要用到树状数组(这些都是小事,but 还挺好用的) 
} */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值