一维树状数组~

来源:互联网 发布:linux退出不保存文件 编辑:程序博客网 时间:2024/05/13 01:19

       树状数组按照一定的规律来存储原数组的一些区间的和,以此可将查询和修改的复杂度都优化到O(logn),规律如下:

       从表面上看,树状数组C[i](1<=i<=n)中,当i为奇数的时候C[i]存的是A[i];当i为偶数时,如果i能表示为2的次幂则C[i]=A[1]+A[2]+···+A[i],如果i不能表示为2的次幂则C[i]=A[i-1]+A[i]。但还有更深层次的规律,其实C[i]=A[i-k+1]+A[i-k+2]+···+A[i](1<=i<=n);k=1<<x,x是i的二进制表示下右边0的个数,即

int lowbit(int i){    return i&(-i);}
       lowbit()函数的返回值就是k。例如,当i=5时,int类型,4字节,占32位:

5的二进制为: 00000000 00000000 00000000 00000101

5的反码为:     11111111 11111111 11111111 11111010

-5的二进制为:11111111 11111111 11111111 11111011

lowbit(5)=1;所以,C[5]=A[5];

PS:

       在计算机中,负数以其正值的补码形式表达。
       反码表示法规定:正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。
       补码表示法规定:正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。


例题:NYOJ_士兵杀敌(二)

题意:插点问线

思路:向后修改,向前求和

CODE:

/****author :Or_me **╭︿︿︿╮{/ a  c /}  ( (oo) )   ︶︶︶ **    ****NYOJ_116题**** 2014 年 7月 31日****/#include <set>#include <map>#include <cmath>#include <queue>#include <stack>#include <vector>#include <cstdio>#include <string>#include <cctype>#include <climits>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;const int MAX=1000001;int c[MAX];int N,M;int lowbit(int t)//lowbit(0)=0,会形成死循环,所以数组下标从1开始{    return t&-t;}void Modify(int m,int n)//向后修改{    for (int k=m; k<=N; k+=lowbit(k))        c[k]+=n;}int sum(int x)//向前求和{    int ans=0;    for (int i=x; i>=1; i-=lowbit(i))        ans+=c[i];    return ans;}int getsum(int i,int j){    return sum(j)-sum(i-1);}int main(){    int m,n,v;    char ch[7];    scanf("%d%d",&N,&M);    for (int i=1; i<=N; i++)    {        scanf("%d",&v);        Modify(i,v);    }    for (int i=1; i<=M; i++)    {        scanf("%s%d%d",ch,&m,&n);        if (ch[0]=='A')            Modify(m,n);        else if (ch[0]=='Q')            printf("%d\n",getsum(m,n));    }    return 0;}

例题:NYOJ_士兵杀敌(四)

题意:插线问点

思路:向前修改,向后求和

CODE:

/****author :Or_me **╭︿︿︿╮{/ a  c /}  ( (oo) )   ︶︶︶ **    ****   题**** 2014 年 月 日****/#include <set>#include <map>#include <cmath>#include <queue>#include <stack>#include <vector>#include <cstdio>#include <string>#include <cctype>#include <climits>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;const int MAX=1000001;int c[MAX];int N,M,T;int lowbit(int t){    return t&-t;}void Modify(int m,int n)//向前修改{    for (int k=m; k>0; k-=lowbit(k))        c[k]+=n;}int sum(int x)//向后求和{    int ans=0;    for (int i=x; i<=M; i+=lowbit(i))        ans+=c[i];    return ans;}int main(){    int a,b,t,x;    char ch[7];    memset(c,0,sizeof(c));    scanf("%d%d",&T,&M);    for (int i=1; i<=T; i++)    {        scanf("%s",ch);        if (ch[0]=='A')        {            scanf("%d%d%d",&a,&b,&t);            Modify(a-1,-t);            Modify(b,t);        }        else        {            scanf("%d",&x);            printf("%d\n",sum(x));        }    }    return 0;}
例题:NYOJ_士兵杀敌(五)

题意:插线问线

思路:树状数组离线版,查询在修改之后

CODE:

/****author :Or_me **╭︿︿︿╮{/ a  c /}  ( (oo) )   ︶︶︶ **    ****   题**** 2014 年 月 日****/#include <set>#include <map>#include <cmath>#include <queue>#include <stack>#include <vector>#include <cstdio>#include <string>#include <cctype>#include <climits>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;const int MAX=1000001;int c[MAX];int main(){    int N,C,Q;    int a,b,x;    scanf("%d%d%d",&N,&C,&Q);    while (C--)    {        scanf("%d%d%d",&a,&b,&x);        c[a]+=x;        c[b+1]-=x;    }    for (int i=1;i<=N;i++)//得到每个士兵的战功    {        c[i]=c[i-1]+c[i];    }    for (int i=1;i<=N;i++)//得到前缀和    {        c[i]=(c[i-1]+c[i])%10003;//保存的是余数    }    while (Q--)    {        scanf("%d%d",&a,&b);        printf("%d\n",(c[b]-c[a-1]+10003)%10003);//+10003,防止结果小于0    }    return 0;}     





0 0
原创粉丝点击