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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【模板】一维树状数组

發(fā)布時(shí)間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【模板】一维树状数组 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

ACM模板


目錄

      • 聊聊前綴和
      • 什么是樹狀數(shù)組?
      • 樹狀數(shù)組相關(guān)操作
      • 局限性
      • 差分在樹狀數(shù)組中的應(yīng)用
        • 區(qū)間更新、單點(diǎn)查詢
        • 區(qū)間更新、區(qū)間查詢
      • 樹狀數(shù)組應(yīng)用

聊聊前綴和

比如數(shù)組

int a[7]={1,2,3,4,5,6,7}

如果需詢問數(shù)組從第l個(gè)數(shù)到第r個(gè)數(shù)的和暴力做法時(shí)間復(fù)雜度為O(n)O(n)O(n)

不過我們可以預(yù)處理一個(gè)前綴和數(shù)組

int b[7]={1,3,6,10,15,21,28}

比如要詢問[l,r]區(qū)間的和我們可以這樣做b[r]-b[l-1]這也時(shí)間復(fù)雜度為O(1)O(1)O(1)

但是問題來了,如果我們要既要修改數(shù)組中元素的值,有要進(jìn)行上述區(qū)間查詢操作呢?

我們發(fā)現(xiàn)每次我們修改原數(shù)組中元素的值時(shí)間復(fù)雜度為O(1)O(1)O(1)但是如果修改前綴和數(shù)組中元素的值時(shí)間復(fù)雜度將會(huì)退化到O(n)O(n)O(n)

總結(jié)一下:

數(shù)組修改元素的值時(shí)間復(fù)雜度區(qū)間求和時(shí)間復(fù)雜度
原數(shù)組O(1)O(n)
前綴和數(shù)組O(n)O(1)

我們可以發(fā)現(xiàn)如果需要單點(diǎn)更新區(qū)間查詢兩種操作時(shí)間復(fù)雜度都是O(n)O(n)O(n)

什么是樹狀數(shù)組?

樹狀數(shù)組是一種便于進(jìn)行單點(diǎn)更新區(qū)間查詢的數(shù)據(jù)結(jié)構(gòu)

樹狀數(shù)組相關(guān)操作

  • 二進(jìn)制中最后一個(gè)1——lowbitlowbitlowbit
  • int lowbit(int x){return x&-x;}
  • 單點(diǎn)更新

    我們對(duì)數(shù)組位置為x的元素加上c

  • //樹狀數(shù)組為tree,數(shù)組元素個(gè)數(shù)為n,數(shù)組下標(biāo)從0開始void add(int x,int c){for(;x<=n;x+=lowbit(x)) tree[x]+=c;}
  • 區(qū)間求和
  • //求出[1,x]數(shù)組中的總和即前綴和int sum(int x){int res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res;}

    局限性

    我們很容易發(fā)現(xiàn)上述樹狀數(shù)組只適用于單點(diǎn)更新區(qū)間查詢,但是如果是區(qū)間修改單點(diǎn)查詢好像力不從心

    差分在樹狀數(shù)組中的應(yīng)用

    告訴你個(gè)好消息如果有差分的介入,那么樹狀數(shù)組可以進(jìn)行區(qū)間更新單點(diǎn)查詢當(dāng)然也可以進(jìn)行更厲害的區(qū)間更新區(qū)間查詢

    區(qū)間更新、單點(diǎn)查詢

    我們把tree[]tree[]tree[]數(shù)組構(gòu)造成一個(gè)差分?jǐn)?shù)組還是看題吧

    題目

    給定長度為N的數(shù)列A,然后輸入M行操作指令。

    第一類指令形如“C l r d”,表示把數(shù)列中第l~r個(gè)數(shù)都加d。

    第二類指令形如“Q X”,表示詢問數(shù)列中第x個(gè)數(shù)的值。

    對(duì)于每個(gè)詢問,輸出一個(gè)整數(shù)表示答案。

    輸入格式

    第一行包含兩個(gè)整數(shù)N和M。

    第二行包含N個(gè)整數(shù)A[i]。

    接下來M行表示M條指令,每條指令的格式如題目描述所示。

    輸出格式

    對(duì)于每個(gè)詢問,輸出一個(gè)整數(shù)表示答案。

    每個(gè)答案占一行。

    #include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int N=100010; ll tree[N]; int n,m; int lowbit(int x) {return x&-x; } void add(int x,int c) {for(;x<=n;x+=lowbit(x)) tree[x]+=c; } ll sum(int x) {int res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res; } int main() {cin>>n>>m;for(int i=1;i<=n;i++){int a;cin>>a;add(i,a);add(i+1,-a);}while(m--){char t;cin>>t;if(t=='Q'){int x;cin>>x;cout<<sum(x)<<endl;}else{int a,b,c;cin>>a>>b>>c;add(a,c);add(b+1,-c);}}return 0; }

    我們可以發(fā)現(xiàn)對(duì)于上述代碼即在建樹的過程中建成差分樹的形式即可

    區(qū)間更新、區(qū)間查詢

    原數(shù)組a[],對(duì)于區(qū)間更新我們可以維護(hù)一個(gè)差分?jǐn)?shù)組b[]

    如果我們維護(hù)數(shù)組a的前綴和我們可以發(fā)現(xiàn)有下面等式:
    ∑i=1xai=∑i=1x∑j=1ibi=∑i=1x(x?i+1)bi\sum_{i=1}^x a_i=\sum_{i=1}^x\sum_{j=1}^i b_i=\sum_{i=1}^x(x-i+1)b_i i=1x?ai?=i=1x?j=1i?bi?=i=1x?(x?i+1)bi?
    變換一下:
    ∑i=1xai=(x+1)∑i=1xbi?∑i=1xbi×i\sum_{i=1}^x a_i=(x+1)\sum_{i=1}^x b_i-\sum_{i=1}^x b_i×i i=1x?ai?=(x+1)i=1x?bi??i=1x?bi?×i

    于是我們可以維護(hù)兩個(gè)差分樹狀數(shù)組tree1[]tree1[]tree1[]維護(hù)$ bi、、 tree2[]維護(hù)維護(hù)護(hù)i*bi$

    給定一個(gè)長度為N的數(shù)列A,以及M條指令,每條指令可能是以下兩種之一:

    1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

    2、“Q l r”,表示詢問 數(shù)列中第 l~r 個(gè)數(shù)的和。

    對(duì)于每個(gè)詢問,輸出一個(gè)整數(shù)表示答案。

    輸入格式

    第一行兩個(gè)整數(shù)N,M。

    第二行N個(gè)整數(shù)A[i]。

    接下來M行表示M條指令,每條指令的格式如題目描述所示。

    輸出格式

    對(duì)于每個(gè)詢問,輸出一個(gè)整數(shù)表示答案。

    每個(gè)答案占一行。

    #include<iostream> #include<algorithm> #include<cstdio> #include<cstring>using namespace std;typedef long long ll;const int N=100010;int n,m; ll tree1[N],tree2[N]; //維護(hù)b[i] 維護(hù)i*b[i] int lowbit(int x) {return x&-x; }void add(ll tree[],int x,ll c) {for(;x<=n;x+=lowbit(x)) tree[x]+=c; }ll sum(ll tree[],int x) {ll res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res; } ll prefix_sum(int x) {return (x+1)*sum(tree1,x)-sum(tree2,x); }int main() {scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int a;scanf("%d",&a);add(tree1,i,a);add(tree1,i+1,-a);add(tree2,i,1ll*i*a);add(tree2,i+1,-1ll*(i+1)*a);}while(m--){char t;int l,r;cin>>t>>l>>r;if(t=='Q'){scanf("%d%d",&l,&r);cout<<prefix_sum(r)-prefix_sum(l-1)<<endl;}else{int d;scanf("%d",&d);add(tree1,l,d),add(tree1,r+1,-d);add(tree2,l,l*d),add(tree2, r+1,-1ll*(r+1)*d);}}return 0; }

    樹狀數(shù)組應(yīng)用

  • 區(qū)間更新區(qū)間查詢

  • 逆序?qū)?/strong>:首先有一種求逆序?qū)Φ姆椒?#xff1a;開一個(gè)數(shù)組cnt[n]?然后遍歷數(shù)組中的每一個(gè)數(shù),在cnt[]數(shù)組中留下標(biāo)記:比如數(shù)組中第i個(gè)元素為x,那就cnt[x]=1,所謂逆序即:我比你先出現(xiàn)而且比你大,對(duì)于上述例子所謂比你大即在[x+1,n]這個(gè)區(qū)間中的值,所謂比你先出現(xiàn)即在第i-1次遍歷時(shí)被標(biāo)記過。轉(zhuǎn)化一下相當(dāng)于求cntcntcnt數(shù)組中[x+1,n]區(qū)間內(nèi)的和

  • 第k小的數(shù)

  • 總結(jié)

    以上是生活随笔為你收集整理的【模板】一维树状数组的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。