閱讀前需掌握的前置知識:樹狀陣列的基本概念和它的單點修改+區間查詢操作。
眾所周知,樹狀陣列這一結構可以很好的解決動態字首和的問題。單點修改+區間查詢是它最常見的套路。但樹狀陣列的強大功能可不止這乙個,今天就為大家介紹它的區間修改+單點查詢功能。不得不佩服,為樹狀陣列賦予這一功能的人腦洞真的是太大。我們想想,怎樣才能把單點查詢轉換為字首和的問題呢?
這裡就要運用到乙個妙不可言的思想:差分。它
大概說的就是算出兩項之差,然後進行處理。
我們把原陣列命名成sum,然後引入delta差分陣列,令delta[i]=sum[i]-sum[i-1]。注意,在這裡預設sum[0]=0。
先陳述乙個事實:
為什麼呢?很簡單,我們把delta陣列之和這個式子轉換一下:
delta[1]+delta[2]+...+delta[i-1]+delta[i]
=sum[1]-sum[0]+sum[2]-sum[1]+...+sum[i-1]-sum[i-2]+sum[i]-sum[i-1]
=sum[0]+sum[i]
=sum[i]
是不是有一種恍然大悟的感覺?~~~
心細的同學可能已經注意到了,由delta陣列維護sum陣列的時候,不就是乙個字首和嗎!!!我們就可以運用樹狀陣列來維護了!具體來說,我們以delta陣列為樹狀陣列來維護,每次更新差分序列,當要查詢某個數時,直接字首和累加就好了。
這裡還有乙個重點,就是如何add給區間加數。這裡也是乙個最巧妙的地方。
我們假設現在有乙個數列:1 5 3 8 9。如果我們把[2,4]這個區間統一加上3,那麼原數列變為:1 8 6 11 9
。現在我們對比來看原數列和現數列的差分序列:原:1 4 -2 5 1 現:1 7 -2 5 -2
我們可以很清楚的看到,除了delta[2]和delta[5],其他差分數都沒有發生變化。如果你再多列舉一些例子,就會發現,若在[l,r]這個區間裡統一加上x,那麼差分序列的改變為:delta[l]+=x,delta[r+1]-=x。那麼這是為什麼呢?很簡單,如果兩個相鄰的元素同時加上乙個數或者同時不改變,那麼他們的差也不會改變。對比整個數列中兩兩相鄰的情況,發現只有這兩組數,沒有進行統一改變,那麼差值自然改變。至於乙個是+=乙個是-=,就不用說了吧,想一想就知道為什麼了~~~
請仔細咀嚼一下這句精妙的話:差分是字首和的逆運算
大體就是這樣子啦!最後總結一下區間修改+單點查詢的要點!
1:注意是維護差分陣列,一習慣就可能打成普通樹狀陣列。
2.add的時候以單點修改+區間查詢為基本框架,只是以差分為思想,每次進行add(l,x)和add(r+1,-x)。
3.查詢第x個位置的數的時候,直接求前x個位置的字首和就好了。
下面給出標準的區間修改+單點查詢的**:
1 #include2view code#define mx 100005
3using
namespace
std;
4int n,ans[mx],tree[mx];int lowbit(int x)
5void add(int x,int q)}
6int query(int
x)return
tot;8}
9int
main()
12for(int i=1;i<=m;i++)
13for(int i=1;i<=k;i++)
14for(int i=1;i<=k;i++)cout
15return0;
16 }
這篇部落格就到這裡啦~~喜歡的請點讚哦~~
樹狀陣列 單點修改,區間查詢(詳解)
問題的提出 給定乙個序列 a,可以進行兩種操作 單點修改,區間查詢 首先,我們會想到直接用乙個現行的陣列。那麼單點修改的時間複雜度將是 o 1 o 1 但是區間查詢的時間複雜度卻是 o n o n 資料範圍一大,就很有可能會超時。那麼,又有人會想到用乙個字首和陣列,但是有沒有想過,雖然區間查詢的時間...
樹狀陣列 區間修改 單點查詢
說一下差分 現在我們有乙個從小到大的數列a a 1 3 6 8 9 然後還有乙個差分陣列b b 1 2 3 2 1 對應 1,3 1,6 3,8 6,9 8,相信某些同學絕已經看出端倪了.這裡b i a i a i 1 我令a 0 0,故b 1 a 1 int now 0,temp scanf d ...
樹狀陣列區間修改,單點查詢
普通的單點修改單點查詢就不講了,從區間修改和單點查詢講起。原來的值存在a裡面,多建立個陣列c1,注意 c1 i a i a i 1 那麼求a i 的值的時候a i a i 1 c1 i a i 2 c1 i c1 i 1 c1 1 c1 2 c1 i 所以就用c1建立樹狀陣列,便可以很快查詢a i ...