差分陣列是個啥?能幹啥?怎麼用?(差分詳解 例題)

2021-09-26 11:00:46 字數 4439 閱讀 4309

差分陣列很明顯就是個陣列唄,,,

本菜雞學的比較淺,先說一下我自己認識的差分陣列吧!

先解釋一下什麼是 差分:

差分其實就是資料之間的差,什麼資料的差呢?就是上面所給的原始陣列的相鄰元素之間的差值,我們令d[i]=a[i+1]-a[i],一遍for迴圈即可將差分陣列求出來。

下面給你乙個栗子,給出乙個差分陣列先

其實差分陣列是乙個輔助陣列,從側面來表示給定某一陣列的變化,一般用來對陣列進行區間修改的操作

還是上面那個表裡的栗子,我們需要進行以下操作:

1、將區間【1,4】的數值全部加上3

2、將區間【3,5】的數值全部減去5

很簡單對吧,你可以進行列舉。但是如果給你的資料量是1e5,操作量1e5,限時1000ms你暴力列舉能莽的過去嗎?t到你懷疑人生直接。這時我們就需要使用到差分陣列了。

其實當你將原始陣列中元素同時加上或者減掉某個數,那麼他們的差分陣列其實是不會變化的。

利用這個思想,咱們將區間縮小,縮小的例子中的區間 【1,4】吧這是你會發現只有 d[1]和d[5]發生了變化,而d[2],d[3],d[4]卻保持著原樣,

在進行下乙個操作,

這時我們就會發現這樣乙個規律,當對乙個區間進行增減某個值的時候,他的差分陣列對應的區間左端點的值會同步變化,而他的右端點的後乙個值則會相反地變化,其實這個很好理解

其實也就這麼一點**就ok了

while(m--)
既然我們要對區間進行修改,那麼差分陣列的作用一定就是求多次進行區間修改後的陣列嘍

注意只能是區間元素同時增加或減少相同的數的情況才能用

因為我們的差分陣列是由原始陣列的相鄰兩項作差求出來的,即 d[i]=a[i]-a[i-1];那麼我們能不能反過來,求得一下修改過後的a[i]呢?

直接反過來即得  a[i]=a[i-1]+d[i]

事實證明這是正確的,具體證法就不再推廣,有空再補上吧;

更新陣列a的方式則是下面的那一點點**,這樣我們就求出來了更新後的陣列 a,是不是比線段樹快多了呢?

for(int i=1;i<=n;i++)

a[i]=a[i-1]+b[i];

翻來覆去還是那句,區間修改,當然了,有時候要結合樹狀陣列來使用。直接看題目吧

這個題果的不能再果了吧,看懂上面的,閉著眼也能敲出來

直接附上**吧

#include#include#include#include#include#include#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

using namespace std;

const int inf=0x3f3f3f3f;

const int mm=1e5+10;

int a[mm],b[mm];

int x,y;

int main()

for(int i=1;i<=n;i++)

a[i]=a[i-1]+b[i];

for(int i=1;i再看下乙個稍微高階一點的題目

這道題他要你求出每頭牛的最大可能的高度,我們的思路就是先讓所有的牛都和最高的一樣高,關於他給到的牛能看到的區間進行修改,讓中間的數至少減一,注意這裡修改的一定是開區間,區間端點不能修改。再乙個就是關於去重問題。。。誰會想到他還有這麼乙個坑呢????

只要看透了是個差分也很簡單,**附上

#include#include#include#include#include#include#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

using namespace std;

const int inf=0x3f3f3f3f;

const int mm=1e4+10;

int a[mm],b[mm];

int vis[mm][mm];

int main()

for(int i=1;i<=n;i++)

return 0;

}

今天測試碰到了乙個二階的差分題,我居然不會,用一階差分直接被t了就很難受,聽了旁邊的大佬才知道,可以用兩個差分來做,膜拜一下orz

首先說一下,這道題的題意吧,反正我一開始模擬了半年也沒看懂,太難受了。

這道題意的坑在於那k個詢問:

其實他給的k個詢問並不是直接讓你修改的區間,而是乙個問題編號的區間,他給的[l,r]是告訴你要執行編號為 l~r 的操作這就告訴我們要把問題存起來先,也算是乙個離線操作吧。這道題顯然可以使用線段樹,但是我覺得要打好多行**,就選擇了剛學的差分,我是用一階差分做的,直接兩層大迴圈,然後t死,這裡有個**,來紀念一下我自己被t慘的下場,不具備參考價值 嘻嘻嘻。這時我們就需要 用兩個差分,第乙個差分來維護要進行的操作的區間端點,就是那k個詢問,這是有點難受的,反正我一上來沒有想到,是旁邊的大佬偷偷告訴我的,第二個差分就是來維護我們的原始陣列,進行修改,修改的方法可以往上看↑↑↑↑↑↑

#include#include#include#include#include#include#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

using namespace std;

const int inf=0x3f3f3f3f;

const ll mm=1e5+10;

ll a[mm],d[mm];

ll op[mm][5];

ll n,m,k;

ll cnt[mm];

ll num[mm];

int main()

for(int i=1;i<=m;i++)

num[i]=num[i-1]+cnt[i];

for(ll i=1;i<=m;i++)

for(ll i=1;i<=n;i++)

a[i]=a[i-1]+d[i];

for(ll i=1;i<=n;i++)

cout還有一道高階題目:

這個題也果的一批,讓你進行區間修改與區間求和查詢,剛學線段樹的時候用線段樹寫的,那是乙個受罪啊,也就寫了兩千多個字元吧,是真的累。。。下面是一片關於這道題線段樹解法的blog

這裡我們講乙個高深一點的做法,那就是差分。但是有一點,差分對區間修改好用,但是區間求和還是需要暴力啊,依舊讓你ttt這就是這道題的高深之處了,那就是結合樹狀陣列,差分負責區間修改,樹狀陣列進行區間求和,下面的**也就888個字元,不過有點費腦,或許用的多了就好了吧!

#include#include#include#include#include#include#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

using namespace std;

const int inf=0x3f3f3f3f;

const ll mm=1e5+10;

ll n,m;

ll a[mm],b[mm],c[mm];

ll sum[mm];

ll lbt(int x)

void update(int pos,int k)

}ll getsum(int pos)

return res;

}int main()

char op[2];

ll x,y,z;

while(m--)

else

} return 0;

}

差分陣列概述

在網上講差分陣列的博文很少,也很難找到。一度以為差分陣列是傳播於小眾的神犇技巧所以一直放著沒有去研習。今天做了 bzoj1635後發現各路神犇都用差分陣列,本蒟卻傻傻寫了線段樹。對於序列a 取a i a i 1 為其差分陣列b i 的值,可以發現,a i bj 1 j i 如 對於序列 a b c ...

港口 差分陣列)

傳送門 思路 因為是區間加減,所以考慮差分陣列,題意變為 要求差分陣列d 2 d 3 d n d 2 d 3 dots d n d 2 d 3 d n 全為0.每次區間加或減會使差分陣列乙個加1,乙個減1,因為要用最小次數,所以每次操作最好產生有效貢獻,可知當為正數或負數的差分陣列變為0後,剩下我們...

差分陣列詳解

學習部落格 題目 來先看一道裸題,有n個數。m個操作,每一次操作,將x y區間的所有數增加z 最後有q個詢問,每一次詢問求出x y的區間和。思路 很明顯,直接用字首和無法快速滿足這個操作,所以我們就用到了差分陣列。設a陣列表示原始的陣列 設d i a i a i 1 1設f i f i 1 d i ...