树状数组

树状数组其实不难 只要明白好了c 数组 问题就迎刃而解

多亏看了这个大佬的博客 让我明白了 转过来 复习算法用 出处:https://www.cnblogs.com/hsd-/p/6139376.html

  1. int lowbit(int t)
  2. {
  3. return t&(-t);
  4. }
  1. void add(int x,int y)
  2. {
  3. for(int i=x;i<=n;i+=lowbit(i))
  4. tree[i]+=y;
  5. }
  1. int getsum(int x)
  2. {
  3. int ans=0;
  4. for(int i=x;i>0;i-=lowbit(i))
  5. ans+=tree[i];
  6. return ans;
  7. }

     这篇笔记 会详细的讲解,使得队员们对树状数组彻底入门  而不是懵懵懂懂。

以上先给出 最常见的,三个函数。(单点更新,区间查询)

     网上的解释以及分析有很多,这里是我的一点总结和体会归纳一下,并且在周三(2016.12.07)的讲座之后会发布在团队笔记中,

     请队员们细细阅读,并且补题

     下面开始

*************************************************分割线

树状数组  重点是在树状的数组

大家都知道二叉树吧

叶子结点代表A数组A[1]~A[8]

 

 .......

现在变形一下

 现在定义每一列的顶端结点C[]数组 

 如下图

 

 

C[i]代表 子树的叶子结点的权值之和// 这里以求和举例

如图可以知道

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];

下面观察如下图

将C[]数组的结点序号转化为二进制

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[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3;

可以自行带入验证;

现在引入lowbit(x) 

lowbit(x) 其实就是取出x的最低位1  换言之  lowbit(x)=2^k  k的含义与上面相同 理解一下

下面说代码

  1. int lowbit(int t)
  2. {
  3. return t&(-t);
  4. }
  5. //-t 代表t的负数 计算机中负数使用对应的正数的补码来表示
  6. //例如 :
  7. // t=6(0110) 此时 k=1
  8. //-t=-6=(1001+1)=(1010)
  9. // t&(-t)=(0010)=2=2^1

C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];

C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];

 

*************************************************分割线

区间查询

ok 下面利用C[i]数组,求A数组中前i项的和 

举个例子 i=7;

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

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[7]=A[1]+A[2]+A[3]+A[4]+A[5] ;   前i项和

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)];

 

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

结合代码

  1. int getsum(int x)
  2. {
  3. int ans=0;
  4. for(int i=x;i>0;i-=lowbit(i))
  5. ans+=C[i];
  6. return ans;
  7. }

对于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[]数组呢?

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

 

结合代码分析

  1. void add(int x,int y)
  2. {
  3. for(int i=x;i<=n;i+=lowbit(i))
  4. tree[i]+=y;
  5. }
  6. //可以发现 更新过程是查询过程的逆过程
  7. //由叶子结点向上更新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]

 

*************************************************分割线

先这样 

讲解题目:

http://poj.org/problem?id=2299   poj 2299 

http://codeforces.com/contest/703/problem/D   cf 703D 

 


例题:

Farmer John's cows have discovered that the clover growing along the ridge of the hill (which we can think of as a one-dimensional number line) in his field is particularly good.

Farmer John has N cows (we number the cows from 1 to N). Each of Farmer John's N cows has a range of clover that she particularly likes (these ranges might overlap). The ranges are defined by a closed interval [S,E].

But some cows are strong and some are weak. Given two cows: cow i and cow j, their favourite clover range is [Si, Ei] and [Sj, Ej]. If Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj, we say that cow i is stronger than cow j.

For each cow, how many cows are stronger than her? Farmer John needs your help!

Input

The input contains multiple test cases.
For each test case, the first line is an integer N (1 <= N <= 10 5), which is the number of cows. Then come N lines, the i-th of which contains two integers: S and E(0 <= S < E <= 10 5) specifying the start end location respectively of a range preferred by some cow. Locations are given as distance from the start of the ridge.

The end of the input contains a single 0.

Output

For each test case, output one line containing n space-separated integers, the i-th of which specifying the number of cows that are stronger than cow i.

Sample Input

3
1 2
0 3
3 4
0

Sample Output

1 0 0

Hint

Huge input and output,scanf and printf is recommended.

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define maxx 100010
int t,c[maxx];
struct p
{
    int a;
    int b;
    int ge;
    int i;
} x[maxx];
int  cmp(struct p a,struct p b)
{
    if(a.b!=b.b)
        return a.b>b.b;
    return a.a<b.a;
}
int cmp1(struct p a,struct p b)
{
    if(a.i!=b.i)
        return a.i<b.i;
}
int lowbit(int x)
{
    x=x&-x;
    return x;
}
void update(int n,int v)
{
    while(n<=maxx)
    {
        c[n]+=v;
        n+=lowbit(n);
    }
}
int getsum( int n)
{
    int ans=0;
    while(n>0)
    {
        ans+=c[n];
        n-=lowbit(n);
    }
    return ans;
}
int main()
{
    int i,a,b,d,ans,j;
    while(~scanf("%d",&t)&&t)
    {
        memset(c,0,sizeof(c));
        memset(x,0,sizeof(x));
        ans=0;
        for(i=1; i<=t; i++)
        {
            scanf("%d%d",&x[i].a,&x[i].b);
            x[i].i=i;
        }
        sort(x+1,x+1+t,cmp);
        for(i=1; i<=t; i++)
        {
            if(i==1)
            {
                x[i].ge=0;
                update(x[i].a+1,1);
            }
            else if(x[i].a==x[i-1].a&&x[i].b==x[i-1].b)
            {
                x[i].ge=x[i-1].ge;
                update(x[i].a+1,1);
            }
            else
            {
                int t1=getsum(x[i].a+1);
                x[i].ge+=t1;
                update(x[i].a+1,1);
            }
            // printf("%d\n",x[i].ge);
        }
        sort(x+1,x+1+t,cmp1);
        for(i=1; i<=t-1; i++)
            printf("%d ",x[i].ge);
        printf("%d\n",x[i].ge);
    }
}

N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

Input

每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。

Output

每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。

Sample Input

3
1 1
2 2
3 3
3
1 1
1 2
1 3
0

Sample Output

1 1 1
3 2 1

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int t,c[110000];
int lowbit(int x)
{
    x=x&-x;
    return x;
}
void update(int a,int b)
{
    while(a>0)
    {
        c[a]+=b;
        a-=lowbit(a);
    }
}
int getsum(int num)
{
    int sum=0;
    while(num<=t)
    {
        sum+=c[num];
        num+=lowbit(num);
    }
    return sum;
}
int main()
{
    int i;
    int a,b;
    while(~scanf("%d",&t)&&t)
    {
        memset(c,0,sizeof(c));
        for(i=1; i<=t; i++)
        {
            scanf("%d%d",&a,&b);
            update(b,1);
            update(a-1,-1);
        }
        for(i=1; i<=t-1; i++)
        {
            printf("%d ",getsum(i));
        }
        printf("%d\n",getsum(t));

    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值