树状数组及其应用(2)

上一篇Blog讲述了树状数组的基本性质以及最典型应用:单点更新与区间求职
本文将给出树状数组的另外两种应用:
(1)区间更新与单点求值!
(2)求逆序对!

首先,来回顾一下什么是单点更新与区间求值:
在该应用中,现状数组的值实际上是存储了简单数组中某一个区间范围内的所有元素的和:
C[4] = A[4]+C[3]+ C[2] = A[1]+A[2]+A[3]+A[4]
在这样的情况下,点更新和区间求值的应用应该是很容易理解的了。。。

区间更新和单点求值:
那么,什么的区间更新和单点求值呢?
简单的说,区间更新是将数组中第i到第j个元素都加上同一个值。单点求值就是求数组中某一个元素的值!
一个例题如下: N个气球排成一排,大牛每次骑上凤凰牌自行车,给a到b之间的气球上色,过了k次后,请问某个气球被涂了几次颜色?
能不能用树状数组C来表示描述该问题呢?有点废话了,当然是可以啦!!!
first: 用树状数组的值来表示第k个气球之后的所有气球的着色次数的变化量!!
Query(i)会返回前i个元素的和,是不是说会返回第i个元素的变化量情况的总和呢?
Second:假设要给(a,b)之间的气球涂色,怎么update呢:
update(a,1)
update(b+1,-1)
解释一下吧,所有a(含a)以后的气球都添加一次,而所有b+1 以后的气球都减掉一次!!!是不是,相当于(a,b)之间的气球加一次色了???
下面是用C++实现的代码啦!和上一篇用Python实现的不一样哦!!

#include <iostream>
#include <string.h>
using namespace std;

int n,tree[1005]; //全局变量,用于存户树状数组


int lowbit(int i) // 求2^k ,不明白?看上一篇
{ return i&(-i);}

int update(int i ,int val) //更新?
{
    while(i<= n)
    {tree[i] = tree[i]+i ; i += lowbit(i);}
}

int query(int i) //查询?
{
    int sum = 0;
    while( i > 0 )
    { sum += tree[i] ; i-= lowbit(i);   }
    return sum;
}

int main()
{
    int T;
    cout<< "有几个气球:";
    cin>>n;
    cout<< "会来回涂几次颜色?";
    cin>>T; //T次图画颜色!
    memset(tree,0,sizeof(tree));

    for(int i = 1; i<= T; i++)
    {
        int a,b;
        cout<< "请输入a和b:"<< endl;
        cin>>a>>b;
        update(a,1);
        update(b+1,-1);
    }
    //查询第i个气球被涂了几次?
    int i ;
    cout<<"你想看第几个气球被涂了几次?"<<endl;
    cin>>i;
    cout<<query(i);
    return 0;
}

说明:这个程序里,假设数组的下标是从1开始的哦!!!


貌似上面那个例子还挺好理解的啊!下面来看看:
逆序对!!!
问题:
Slerk有N头牛,一字排开,在晚上挤奶,每个牛有一个唯一的坏脾气值,值的范围为1……1000.现在sherk想按照牛的脾气值把牛排序,排序时只能将相邻的牛交换位置,而且移动两头坏脾气为x和y的牛需要时间为x+y,问,怎样能求出重新排序的最短时间?

分析:
首先,这是一个逆序对问题!对不对?只能交换相邻的牛,又要交换的次数最少!!!不就是逆序对1-1交换么?
那么,首先要求逆序对!!!
用树状数组A[i]来记录数值a出现的次数,Query(a)是不是能返回所有小于等于a的数的个数?
那么,假设数a是出现的第i个数,在已经出现的数中有多少个比a大的数呢?
i - query(a) !!! 是不是呢?

那么整个数组中有多少逆序对呢? 看下面的代码?

for(int i = 1; i <= n ; i++)
{
    cin>>a;  //依次读入第i个元素a
    update(a,1);  // a出现的次数加1咯
    ans+= i- query(a);  // 有多少比a大的数已经出现了呢?
}
cout<<ans; //逆序对的数量出来啦

在解决牛的移动问题时,除了要知道怎么换(逆序对咯),还要记录完成交换的代价!!!
代价 = X + Y , isn’t is?
那么对与一个数值为a,且需要交换k次的牛,完成它的交换一共需要多少代价呢?
是不是 a*k+ sum(所有已经出现的,且比a大的值的和)
那么我们是不是可以用另一个树状数组来计算大于等于a的元素的和呢?(想想单点更新,区域求和?)
OK!!!屡屡思路,来看一下下面的代码把:

#include <iostream>
#include <string.h>
using namespace std;

int lowbit(int i) // 求2^k ,不明白?看上一篇
{ return i&(-i);}

int update(int *tree, int t_size, int i ,int val) //更新?
{
    while(i<= t_size)
    {tree[i] = tree[i]+val ; i += lowbit(i);}
}

int query(int *tree, int i) //查询?
{
    int sum = 0;
    while( i > 0 )
    { sum += tree[i] ; i-= lowbit(i);   }
    return sum;
}

int main()
{
    int N;
    int MAX_DJ = 1000;   //我们直接假设牛的脾气值最大就是1000啦
    cout<<"有多少头牛呢?";
    cin>>N;
    int ans = 0;
    int* treeNum = new int[N+1];
    int* treeDJ = new int[MAX_DJ+1];
    memset(treeNum, 0 , sizeof(int)*(N+1) );
    memset(treeDJ, 0 , sizeof(int)*(MAX_DJ+1) );
    for(int i = 0; i< N; i++)
    {
        int a;
        cout<<"输入第"<<i+1<<"头牛的脾气";
        cin>>a;
        update(treeNum,N,a,1);
        update(treeDJ,MAX_DJ, a, a);
        int  k1 = (i+1) - query(treeNum,a);
        if(k1 != 0)
        {

            int k2 = query(treeDJ,MAX_DJ) - query(treeDJ,a);
            ans += k1*a + k2;
        }
    }
    cout<< "res:"<< ans;

    delete [] treeNum;
    delete [] treeDJ;
}

暂时就写到这里啦!!!
让我也回过头思考一下!!!
下一篇会谈谈其他的求逆序对的方法!!!

                                2016-5-27
                                TJU-26E
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值