1. 程式人生 > >神奇的差分法(內附樹狀陣列的一點擴充套件)

神奇的差分法(內附樹狀陣列的一點擴充套件)

差分法是我們所用的一個強力的武器!

有這把武器你就可以統治世界。。。

一個大佬曾經講過,一但碰到區間修改的題,就要優先考慮差分。

目錄

  1. 普通差分法
  2. 差分套差分(二階差分)
  3. 高階差分
  4. 樹上差分(點的意義與邊的意義)
  5. 例題

普通差分法

我們有時做題,會發現這麼一種題。

給你長度為n的序列,m次操作,有兩種:1. 讓[l,r]區間加上k。2. 查詢一個點的值。

典型的區間修改,單點查詢。
單點查詢簡單。
但是區間修改怎麼做?
暴力卡常。。。
線段樹。。。
比較正經的做法是差分,差分是什麼?

在這裡插入圖片描述

對於一個序列a,定義另一個序列b, b

[ i ] = a [ i ] a [
i 1 ] b[i]=a[i]-a[i-1] ,則叫b陣列為a陣列的差分陣列,這樣有什麼優勢呢?

首先,如果要求a[i],我們會發現就是 b [

i ] + a [ i 1 ] b[i]+a[i-1] ,不斷拆開, a [ i ] = b [ i ] + b [ i 1 ] + b [ i 2 ] + b [ i 3 ] + . . . . + b [ 1 ] a[i]=b[i]+b[i-1]+b[i-2]+b[i-3]+....+b[1] ,是不是很棒棒,就是字首和!

但是單點查詢O(n)呀

先講這樣怎麼修改,假設是 [ l , r ] [l,r] 區間加上k,那麼我們就讓 b [ l ] b[l] 加上 k k ,讓 b [ r + 1 ] b[r+1] 減去 k k 就行了。這樣,在 [ l , r ] [l,r] 區間內,每個數都會加上 b [ l ] b[l] 多出的 k k ,但是在 r r 之後,我們也會因為 b [ r + 1 ] b[r+1] 減去了 k k 從而和 b [ l ] b[l] k k 抵消。

這樣,區間修改就完成了!

但是單點查詢又複雜了,它可以表達成字首和,字首和…樹狀陣列維護就可以了!
是不是很不錯!

這裡給大家一點擴充套件!

至於樹狀陣列區間查詢,區間修改,我粘上一個大佬的話,我認為很不錯!


我們還是需要引入delta陣列,這裡的delta[i]表示區間a[i…j]都需要加上的值的和。那麼當我們需要將區間[l,r]上的每個數都加上x時,我們還是可以直接在樹狀陣列上將delta[l]加上x,delta[r+1]減去x。

那麼問題來了,如何查詢區間[l,r]的和?

我們設a[1…i]的和為sum[i],根據delta陣列的定義,則:

s u m [ i ] = j = 1 i a [ j ] + j = 1 i d e l t a [ j ] ( i j + 1 ) sum[i]=\sum_{j=1}^ia[j]+\sum_{j=1}^idelta[j]*(i-j+1)
s u m [ i ] = j = 1 i a [ j ] + ( i + 1 ) j = 1 i d e l t a [ j ] j = 1 i d e l t a [ j ] j sum[i]=\sum_{j=1}^ia[j]+(i+1)*\sum_{j=1}^idelta[j]-\sum_{j=1}^idelta[j]*j

這樣我們就不難看sum[i]是由哪三個部分組成的了。我們需要用一個asum陣列維護a陣列的字首和,delta1與delta2兩個樹狀陣列,delta1維護delta陣列的和,delta2維護delta[i]*i的和,程式碼如下:

void add(int *arr int pos,int x){
    while(pos<=n) arr[pos]+=x,pos+=lowbit(pos);
}
void modify(int l,int r,int x){
    add(d1,l,x),add(d1,r+1,-x),add(d2,l,x*l),add(d2,r+1,-x*(r+1));
}
int getsum(int *arr,int pos){
    int sum=0;
    while(pos) sum+=arr[pos],pos-=lowbit(pos);
    return sum;
}
int query(int l,int r){
    return asum[r]+r*getsum(d1,r)-getsum(d2,r)-(asum[l-1]+l*getsum(d1,l-1)-getsum(d2,l-1));
}

摘自


咳咳,迴歸正題,總結一下
普通差分就是這個數減去前一個數所得到的一個數組,他不是個演算法,只是種技巧,比如在樹狀陣列中的妙用,讓樹狀陣列具有區間查詢,單點修改的功能。

差分套差分(二階差分)

沒錯你沒有聽錯,差分都可以套了!

好像又叫二階差分。

怎麼套?將差分陣列再差分一遍,求到了差分套差分的陣列,定位c陣列。

那麼,推一推,發現 a [ i ] = c [ i ] 1 + c [ i 1 ] 2 + . . . + c [ 1 ] ( i 1 ) a[i]=c[i]*1+c[i-1]*2+...+c[1]*(i-1)

嗯,這個有什麼用呢?

如果有一個毒瘤出題人,出了一道題(就是我被坑了,就寫出來了):

給你一個長度為n的序列a,有m次操作,每次操作讓區間[l,r]分別加上t,t*2,t*3,...,t*(r-l+1)
最後輸出a序列的每個數的值

把1操作中加上的數差分,就為 t , t , t , t , t , . . . , t t,t,t,t,t,...,t (注意:以後求高階差分的修改公式,將加上的陣列也進行差分來推是最好的!),那麼,就等於給a的差分陣列b區間加上t,那麼就將b再差分出另一個差分陣列c來更改,最後O(n)輸出一下答案就好了。

當然,相比差分,差分套差分會有更多應用,歡迎大家探究!

高階差分

這個就很毒瘤了,一般沒有人出這種題。講一下就是希望以後有什麼人出這種毒瘤題

我們通過列三階差分,設陣列為d,則有 a [ i ] = 1 d [ i ] + 3 d [ i 1 ] + 6 d [ i 2 ] + 10 d [ i 3 ] + . . . . . . a[i]=1*d[i]+3*d[i-1]+6*d[i-2]+10*d[i-3]+...(猴子的腦子燒焦了...)
我們觀察到 3 1 = 2 , 6 3 = 3 , 10 6 = 4... 3-1=2,6-3=3,10-6=4... 這不是二階等差數列嗎?

繼續觀察:

我們發現n階差分陣列單點查詢就等於n-1階差分陣列的字首和,也就是說,而我們發現從三階開始, d [ i ] = c [ i ] + c [ i 1 ] + c [ i 2 ] . . + c [ 1 ] d[i]=c[i]+c[i-1]+c[i-2]..+c[1]

相關推薦

神奇陣列一點擴充套件

差分法是我們所用的一個強力的武器! 有這把武器你就可以統治世界。。。 一個大佬曾經講過,一但碰到區間修改的題,就要優先考慮差分。 目錄 普通差分法 差分套差分(二階差分) 高階差分 樹上差分(點的意義與邊的意義) 例題 普通差分法

CodeForces 1045G AI robotsCDQ分治 + 陣列 + 單調佇列

大致題意:有很多個機器人,他們要相互交流有一些限制條件。首先是,兩個人要相互能夠能夠看到;其次,兩個人的智商的差不超過K。現在給出每個機器人的視力範圍和他們的智商,現在問你總共有多少對機器人能夠相互交流。 首先來看下總共有多少個限制條件。由於是要求雙方都能夠看到

強化學習用時序TD求解

bili 通過 信號 老鼠 不同的 有著 ren emp 重定義     在強化學習(四)用蒙特卡羅法(MC)求解中,我們講到了使用蒙特卡羅法來求解強化學習問題的方法,雖然蒙特卡羅法很靈活,不需要環境的狀態轉化概率模型,但是它需要所有的采樣序列都是經歷完整的狀態序列。如果我

TD Temporal-Difference Learning 時序學習

temporary 英 ['temp(ə)rərɪ]美 [ˈtempəreri] adj. 臨時的,暫時的;短暫的 n. 臨時工,臨時僱 TD演算法是RL的核心演算法。TD是DP和MC演算法的結合。Like DP, TD methods without waiting for a fin

ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang 塊+陣列+dfs序

題意 給你一顆樹,由兩種操作: 1.把這棵樹深度為 D D D的點全部都加上一個值。 2.求以p為根節點的子樹的權值和是多少? 思路 對於樹上的東西,我們可以把他求一下DFS序,之後就可以把樹上的結構變成

簡單版數組,線段

cli i++ lose 預處理 查詢 lan \n strong 格式 前言 首先,在NOIP的比賽裏分塊是一個很好的水分神器,因為它可以代替樹狀數組,線段樹,但是如果出題人要卡你的程序的話...... 分塊思想 包含n個元素的整數數組A,每次可以 C(i, j)

ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang 塊+陣列+dfs序

題意 給你一顆樹,由兩種操作: 1.把這棵樹深度為DD的點全部都加上一個值。 2.求以p為根節點的子樹的權值和是多少? 思路 對於樹上的東西,我們可以把他求一下DFS序,之後就可以把樹上的結構變成線性的結構,之後就是查詢和修改這兩個東西了。 關於修改

親戚【並查集】並查集基本操作

【問題描述】   何氏家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親

CF587F Duff is MadAC自動機+陣列+

考慮兩一個暴力 1 因為詢問\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把詢問離線,然後就是求\([1,x]\)中被\(S_i\)包含的串的數量。考慮當\([1,x-1]->[1,x]\)時我們把\(S_x\)結束節點在fail樹的子樹加1。然後詢問就是求\(S_i\)在

並查集——簡單易懂並查集刪除操作

並查集 並查集的定義 並查集是一種樹型的資料結構,用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題。常常在使用中以森林來表示。 集就是讓每個元素構成一個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。 並查集的用途 維護一個無

記憶體:你跑慢點行不行?CPU:跑慢點你養我嗎?記憶體:我不管!超全思維導圖

![](https://img2020.cnblogs.com/blog/1515111/202003/1515111-20200309131902610-1427836925.png) `主存(RAM)` 是一件非常重要的資源,必須要認真對待記憶體。雖然目前大多數記憶體的增長速度要比 IBM 7094 要

異或和權值陣列

異或和(權值樹狀陣列) 題目描述 在加里敦中學的小明最近愛上了數學競賽,很多數學競賽的題都是與序列的連續和相關的。所以對於一個序列,求出它們所有的連續和來說,小明覺得十分的簡單。但今天小明遇到了一個序列和的難題,這個題目不僅要求你快速的求出所有的連續和,還要快速的求出這些連續和的異或值。小明很快的就求出了

codeforces869EThe Untended Antiquity二維陣列

/* 二維樹狀陣列+Hash 題意: 給一個地圖(n,m) 三種操作: 1,在以(r1,c1)、(r2,c2)為對角的矩形四條邊上新增障礙 2,消除以(r1,c1)、(r2,c2)為對角的矩形四條邊上的障礙 3,判斷(r1,c1)到(r2,c2)是否存在一條路徑,不經過障礙 利用二維樹

CodeForces - 652D Nested Segments線段/陣列+離散化

題目連結 看了大佬的部落格:https://blog.csdn.net/chenquanwei_/article/details/79137969;   題意:給n個線段的左右端點,問每個線段包括多少線段;類似於https://blog.csdn.net/weixin_4275

[BZOJ2527][POI2011]Meteors整體二分+陣列

Address 洛谷P3527 BZOJ2527 LOJ#2169 Solution 容易想到對於每個詢問二分答案,但一次判定是 O (

BZOJ3110[Zjoi2013]K大數查詢陣列+整體二分

3110 [Zjoi2013]K大數查詢 有N個位置,M個操作。操作有兩種,每次操作如果是1 a b c的形式表示在第a個位置到第b個位置,每個位置加入一個數c如果是2 a b c形式,表示詢問從第a個位置到第b個位置,第C大的數是多少。 Input 第一行N,M接下來M行,每行形

【BZOJ1818】[CQOI2010]內部白點陣列,掃描線

【BZOJ1818】[CQOI2010]內部白點(樹狀陣列,掃描線) 題面 BZOJ 題解 不難發現\(-1\)就是在搞笑的。 那麼對於每一行,我們顯然可以處理出來最左和最右的點,那麼等價於我們在橫著的方向上得到了若干條線段,同理,在豎直方向上也得到了若干條線段,那麼最終的答案就是這些線段的交點個數加

Codeforces1076E. Vasya and a Treedfs+離線+陣列維護

題目連結:傳送門 題目: E. Vasya and a Tree time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output

51nod 1081 子段求和線段 | 陣列 | 字首和

題目連結:子段求和 題意:n個數字序列,m次詢問,每次詢問從第p個開始L長度序列的子段和為多少。 題解:線段樹區間求和 | 樹狀陣列區間求和 線段樹: 1 #include <cstdio> 2 #define LC(a) ((a<<1)) 3

A - Ping pong陣列+順序對

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 81   Accepted Sub