日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

树状数组学习小结

發布時間:2025/6/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树状数组学习小结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

樹狀數組,又稱二進制索引樹,英文名Binary Indexed Tree。

一、樹狀數組的用途

主要用來求解數列的前綴和,a[0]+a[1]+...+a[n]。

由此引申出三類比較常見問題:

1、單點更新,區間求值。(HDU1166)

2、區間更新,單點求值。(HDU1556)

3、求逆序對。(HDU2838)

?

二、樹狀數組的表示

1、公式表示

設A[]為一個已知的數列。C[]為樹狀數組。則會有

C[i]=A[j]+...+A[i];j=i&(-i)=i&(i^(i-1))。

2、圖形表示

(注:1、最下面的一行表示數組A,上面的二進制表示的部分是C;

2、圖片來源于http://hi.baidu.com/rain_bow_joy/blog/item/569ec380c39730d2bc3e1eae.html)

?

從以上可以發現:

1、樹狀數組C是表示普通數組A的一部分的和。

2、小標為奇數時,C[i]只能管轄一個A[i]。

3、C[i]的最后一個數一定是A[i]。

?

三、樹狀數組的關鍵代碼

1、

[cpp]?view plaincopyprint?
  • int?lowBit(int?x)??
  • {??
  • ????return?x&(-x);??
  • }??
  • 這段代碼可以簡單的理解為是樹狀數組向前或向后衍生是用的。

    向后主要是為了找到目前節點的父節點,比如要將C[4]+1,那么4+(4&(-4))=8,C[8]+1,8+(8&(-8))=16,

    C[16]+1。

    向前主要是為了求前綴和,比如要求A[1]+...+A[12]。那么,C[12]=A[9]+...+A[12];然后12-12&(-12)=8,

    C[8]=A[1]+...+A[8]。

    ?

    2、

    [cpp]?view plaincopyprint?
  • void?modify(int?pos,int?num)??//pos為數組下標位置,num為要增加的值???
  • {??
  • ????while(pos<=n)???//n為數組的長度???
  • ????{??
  • ????????c[pos]+=num;??
  • ????????pos+=lowBit(pos);??
  • ????}??
  • }??
  • 這段代碼是用來更新樹狀數組的,包括區間更新、單點更新。

    就是想剛才所說的,一點更新了,要不斷將父節點也更新。

    ?

    3、

    [cpp]?view plaincopyprint?
  • int?getResult(int?pos)??//求A[1]+...+A[pos]???
  • {??
  • ????int?sum=0;???
  • ????while(pos>0)??
  • ????{??
  • ????????sum+=c[pos];??
  • ????????pos-=lowBit(pos);??
  • ????}??
  • ??????
  • ????return?sum;???
  • }??????????????????
  • 這段代碼用來求解前綴和的。

    就像剛才說的,求解A[1]+...+A[12],也就是C[12]+C[8]搞定。

    ?

    四、樹狀數組的優點

    1、原本的長度為n的數列求和時間復雜度為O(n),更改的時間復雜度為O(1)。

    樹狀數組將其優化為O(logn)。在n較大時,效率更高。

    2、樹狀數組編碼簡單。

    ?

    五、注意

    1、樹狀數組的下標要從1開始。

    2、在學習的過程中遇到這么個問題。不知道為什么pos+pos&(-pos)就到了pos的父節點,也不知道

    為什么pos-pos&(-pos)就得到了下一個無聯系的節點,從而可以得到前綴和。

    我只能說:我不懂如何證明,這是數學問題了,樹狀數組的發明者應該就是發現了這點才搞出樹狀

    數組的吧。初學者不妨拋開這點,專注于事實,將上面的圖形自己計算畫一遍,非常有利于理解。

    ?

    六、符代碼:

    HDU1166

    單點更新,區間求值

    [cpp]?view plaincopyprint?
  • #include<iostream>??
  • using?namespace?std;??
  • ??
  • const?int?maxn=50001;??
  • ??
  • int?a[maxn];??
  • int?c[maxn];??
  • int?n;???
  • ??
  • int?lowBit(int?t)??
  • {??
  • ????return?t&(-t);??
  • }??
  • ??
  • void?modify(int?t,int?num)??
  • {??
  • ????while(t<=n)??
  • ????{??
  • ????????c[t]+=num;??
  • ????????t+=lowBit(t);??
  • ????}??
  • }??
  • ??
  • int?getResult(int?t)??
  • {??
  • ????int?num=0;???
  • ????while(t>0)??
  • ????{??
  • ????????num+=c[t];??
  • ????????t-=lowBit(t);??
  • ????}??
  • ??????
  • ????return?num;???
  • }??
  • ??
  • void?init()??
  • {??
  • ????for(int?i=1;i<=n;i++)??
  • ????{??
  • ????????scanf("%d",&a[i]);??
  • ??????????
  • ????????modify(i,a[i]);???
  • ????}??
  • }??
  • ??
  • ??
  • int?main()??
  • {??
  • ????int?cas,Case=1;??
  • ??????
  • ????scanf("%d",&cas);???
  • ????while(cas--)??
  • ????{???
  • ????????memset(c,0,sizeof(c));???
  • ????????printf("Case?%d:\n",Case++);??
  • ???????????
  • ????????scanf("%d",&n);??
  • ??????????
  • ????????init();??
  • ??????????
  • ????????char?ch[15];??
  • ????????int?a,b;?????
  • ????????while(scanf("%s",&ch),strcmp(ch,"End"))??
  • ????????{???
  • ????????????scanf("%d%d",&a,&b);??
  • ???????????????
  • ????????????switch(ch[0])??
  • ????????????{??
  • ????????????????case?'Q':??
  • ????????????????????printf("%d\n",getResult(b)-getResult(a-1));??
  • ????????????????????break;???
  • ????????????????case?'A':???
  • ????????????????????modify(a,b);??
  • ????????????????????break;??
  • ????????????????case?'S':??
  • ????????????????????modify(a,-b);??
  • ????????????????????break;??
  • ????????????}??
  • ?????????}??
  • ?????}??
  • ???????
  • ?????system("pause");??
  • ?????return?0;??
  • }???
  • ?

    HDU1556

    區間更新,單點求值

    [cpp]?view plaincopyprint?
  • #include<iostream>??
  • #include<cstring>??
  • using?namespace?std;??
  • ??
  • const?int?maxn=100001;??
  • ??
  • int?c[maxn];??
  • int?n;??
  • ??
  • int?lowbit(int?t)??
  • {??
  • ????return?t&(-t);??
  • }??
  • ??
  • void?insert(int?t,int?d)??
  • {??
  • ????while(t<=n)??
  • ????{??
  • ????????c[t]+=d;??
  • ????????t+=lowbit(t);??
  • ????}??
  • }??
  • ??
  • int?getSum(int?t)??
  • {??
  • ????int?sum=0;??
  • ????while(t>0)??
  • ????{??
  • ????????sum+=c[t];??
  • ????????t-=lowbit(t);??
  • ????}??
  • ??????
  • ????return?sum;??
  • }??
  • ??
  • int?main()??
  • {??
  • ????while(cin>>n,n)??
  • ????{??
  • ????????int?a,b;??
  • ????????memset(c,0,sizeof(c));??
  • ??????????
  • ????????for(int?i=1;i<=n;i++)??
  • ????????{??
  • ????????????scanf("%d%d",&a,&b);??
  • ??????????????
  • ????????????insert(a,1);??
  • ????????????insert(b+1,-1);??
  • ????????}??
  • ??????????
  • ???????for(int?j=1;j<n;j++)??
  • ???????{??
  • ????????????printf("%d?",getSum(j));??
  • ???????}??
  • ???????printf("%d\n",getSum(n));??
  • ????}??
  • ??????
  • ????system("pause");??
  • ????return?0;??
  • }??
  • ?

    HDU2838

    求逆序對

    [cpp]?view plaincopyprint?
  • #include<iostream>??
  • #include<cstring>??
  • using?namespace?std;??
  • ??
  • const?int?maxn=100001;??
  • ???
  • struct?node??
  • {??
  • ????int?cnt;??
  • ????__int64?sum;??
  • }tree[maxn];???????????
  • ??????????
  • int?n;??
  • ??
  • int?lowBit(int?x)??
  • {??
  • ????return?x&(-x);??
  • }??
  • ??
  • void?modify(int?x,int?y,int?t)??
  • {??
  • ????while(x<=n)??
  • ????{??
  • ????????tree[x].sum+=y;??
  • ????????tree[x].cnt+=t;??//tree[].cnt來保存是否出現過a???
  • ????????x+=lowBit(x);??
  • ????}??
  • }??
  • ??
  • __int64?query_cnt(int?x)???//比x小的數的個數???
  • {??
  • ????__int64?sum=0;??
  • ????while(x>0)??
  • ????{??
  • ????????sum+=tree[x].cnt;??
  • ????????x-=lowBit(x);??
  • ????}??
  • ??????
  • ????return?sum;??
  • }??
  • ??
  • __int64?query_sum(int?x)??//比x小的所有數之和???
  • {??
  • ????__int64?sum=0;??
  • ????while(x>0)??
  • ????{??
  • ????????sum+=tree[x].sum;??
  • ????????x-=lowBit(x);??
  • ????}??
  • ??????
  • ????return?sum;??
  • }??
  • ??
  • int?main()??
  • {??
  • ????while(~scanf("%d",&n))??
  • ????{??
  • ????????int?a;??
  • ????????__int64?ans=0;???
  • ????????memset(tree,0,sizeof(tree));??
  • ???????????
  • ????????for(int?i=1;i<=n;i++)??
  • ????????{??
  • ????????????scanf("%d",&a);??
  • ??????????????
  • ????????????modify(a,a,1);??//以a為下標更新數組???
  • ??????????????
  • ????????????__int64?k1=i-query_cnt(a);???//k1為前i個數比a大的數的個數???
  • ????????????if(k1!=0)??
  • ????????????{??
  • ????????????????__int64?k2=query_sum(n)-query_sum(a);?//目前所有數的和-目前所有比a小的數的和,為比a大的數的和?????
  • ????????????????ans+=k1*a+k2;???//調換a所需的時間???
  • ????????????}??
  • ????????}??
  • ??????????
  • ????????printf("%I64d\n",ans);???
  • ????}??
  • ??????
  • ????system("pause");??
  • ????return?0;??
  • }???
  • ?

    七、二維樹狀數組

    C[x][y]=sum(A[i][j])。其中,x-lowBit(x)+1<=i<=x,y-lowBit(y)+1<=j<=y。

    例題:HDU1892

    二維樹狀數組一般就是對矩陣的操作,更新、求值。。。

    代碼:

    [cpp]?view plaincopyprint?
  • #include<iostream>??
  • #include<cstring>??
  • using?namespace?std;??
  • ??
  • const?int?maxn=1005;??
  • ??
  • int?c[maxn][maxn];??
  • ??
  • int?lowBit(int?x)??
  • {??
  • ????return?x&(-x);??
  • }??
  • ??
  • void?modify(int?x,int?y,int?val)??
  • {??
  • ????for(int?i=x;i<maxn;i+=lowBit(i))??
  • ????{??
  • ????????for(int?j=y;j<maxn;j+=lowBit(j))??
  • ????????{??
  • ????????????c[i][j]+=val;??
  • ????????}??
  • ????}??
  • }??
  • ??
  • int?getResult(int?x,int?y)??
  • {??
  • ????int?sum=0;??
  • ????for(int?i=x;i>0;i-=lowBit(i))??
  • ????{??
  • ????????for(int?j=y;j>0;j-=lowBit(j))??
  • ????????{??
  • ????????????sum+=c[i][j];??
  • ????????}??
  • ????}??
  • ??????
  • ????return?sum;??
  • }??
  • ??
  • int?getVal(int?x,int?y)??
  • {??
  • ????return?getResult(x,y)-getResult(x-1,y)-getResult(x,y-1)+getResult(x-1,y-1);??
  • }??
  • ??
  • void?init()??
  • {??
  • ????memset(c,0,sizeof(c));??
  • ??????
  • ????for(int?i=1;i<maxn;i++)??
  • ????{??
  • ????????for(int?j=1;j<maxn;j++)??
  • ????????{??
  • ????????????modify(i,j,1);??
  • ????????}??
  • ????}??
  • }??
  • ??????
  • int?main()??
  • {??
  • ????int?cas,cas1=1,query;??
  • ??????
  • ????scanf("%d",&cas);??
  • ????while(cas--)??
  • ????{??
  • ????????init();??
  • ??????????
  • ????????scanf("%d",&query);??
  • ??
  • ????????printf("Case?%d:\n",cas1++);??
  • ????????for(int?i=1;i<=query;i++)??
  • ????????{??
  • ????????????char?ch;??
  • ????????????int?x1,y1,x2,y2,n;??
  • ??????????????
  • ????????????getchar();??
  • ????????????scanf("%c",&ch);??
  • ??????????????
  • ????????????switch(ch)??
  • ????????????{??
  • ????????????????case?'S':??
  • ????????????????????{??
  • ????????????????????scanf("%d%d%d%d",&x1,&y1,&x2,&y2);??
  • ????????????????????int?x11=min(x1,x2);??
  • ????????????????????int?x22=max(x1,x2);??
  • ????????????????????int?y11=min(y1,y2);??
  • ????????????????????int?y22=max(y1,y2);??
  • ????????????????????printf("%d\n",getResult(x22+1,y22+1)-getResult(x11,y22+1)-getResult(x22+1,y11)+getResult(x11,y11));??
  • ????????????????????break;??
  • ????????????????????}??
  • ????????????????case?'A':??
  • ????????????????????{??
  • ????????????????????scanf("%d%d%d",&x1,&y1,&n);??
  • ????????????????????modify(x1+1,y1+1,n);??
  • ????????????????????break;??
  • ????????????????????}??
  • ????????????????case?'M':??
  • ????????????????????{??
  • ????????????????????scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&n);??
  • ????????????????????int?v=getVal(x1+1,y1+1);??
  • ????????????????????int?Min=min(n,v);??
  • ????????????????????modify(x1+1,y1+1,-Min);??
  • ????????????????????modify(x2+1,y2+1,Min);??
  • ????????????????????break;??
  • ????????????????????}??
  • ????????????????case?'D':??
  • ????????????????????{??
  • ????????????????????scanf("%d%d%d",&x1,&y1,&n);??
  • ????????????????????int?v=getVal(x1+1,y1+1);??
  • ????????????????????int?Min=min(v,n);??
  • ????????????????????modify(x1+1,y1+1,-Min);??
  • ????????????????????break;??
  • ????????????????????}??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??????
  • ????system("pause");??
  • ????return?0;??
  • }??

  • ?

    八、參考文章
    http://mindlee.net/2011/07/10/binary-indexed-trees/?

    http://dongxicheng.org/structure/binary_indexed_tree/

    總結

    以上是生活随笔為你收集整理的树状数组学习小结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。