區間修改查詢問題一般會想到用線段樹或者樹狀陣列來做,但是題目是離線查詢,即完成修改後再查詢的話,可以用到差分陣列。
對於陣列a[i],我們令d[i]=a[i]-a[i-1] (特殊的,第乙個為d[1]=a[1]),則d[i]為乙個差分陣列。
我們發現統計d陣列的字首和sum陣列,有
sum[i]=d[1]+d[2]+d[3]+...+d[i]=a[1]+a[2]-a[1]+a[3]-a[2]+...+a[i]-a[i-1]=a[i],即字首和sum[i]=a[i];
因此每次在區間[l,r]增減x只需要令d[l]+x,d[r+1]-x,就可以保證[l,r]增加了x,而對[1,l-1]和[r+1,n]無影響。 複雜度則是o(n)的。
hdu 1556 color the ball
題意:給出乙個區間n, n個數初始值都是0, 給出n個區間修改,修改是在[l,r]上加1, 最後輸出n個數。
分析:屬於離線查詢, 可以維護差分陣列來求出最後的數字。
#includeusingnamespace
std;
#define inf 0x3f3f3f3f
const
int n = 100000 + 10
;int
m, q;
long
long
a[n], d[n], sum[n];
intmain()
memset(sum,
0, sizeof
(sum));
for (int i = 1; i <= n; ++i)
for (int i = 1; i <= n; ++i)
puts(
"");
}return0;
}
bzoj 3043 incdec sequence
題意:給定乙個長度為n的數列a[i],每次可以選擇乙個區間[l,r],使這個區間內的數都加一或者都減一。
求:(1)至少需要多少次操作才能使數列中的所有數都一樣。
(2)在保證最少次數的前提下,最終得到的數列有多少種
分析:因為我們最終是求乙個所有數的一樣的序列, 所以其差分陣列就是除了d[1]外,d[2]~d[n]全為0。
然後題目中區間加一或者區間減一操作, 其實就是在差分陣列中, 找出兩個下標i, j, d[i]++, d[j]--(或者d[i]--, d[j]++)
所以我們就可以利用這個操作把d[2]~d[n]中的正數和負數中和, 然後還有會一部分正數(或負數)多出來, 我們只需要在這個點上面直接加減就好(這個單點操作其實有兩重意義,涉及第二問)
我們求出d[2]~d[n]的正數和絕對值pos, 負數和絕對值neg
所以第一問的答案就是max(pos,neg)
剛剛提到了有乙個單點操作的意義, 我們就拿d[i]++來說吧
第一種含義是, 因為d[1]是什麼並沒有關係, 所以可以代表d[1]-- , d[i]++, 實際意義就是陣列a[1]~a[i-1]都減去1。
第二種含義是, 因為修改區間[l,r]加一是在d[l]++, d[r+1]--, 如果這個r等於n的時候, 那麼r+1並沒有意義, 所以也成了單點操作, 實際意義就是a[i]~a[n]加上1。
就是說那個多出來的d[i]可以配給d[1]或者d[n+1](沒有意義)
所以我們的問題二可以轉化為d[1]的取值個數, 那麼可以配0次, 1次...到abs(pos-neg)次, 所以最終d[1]的取值總共abs(pos-neg) + 1次。
感謝:neord
差分陣列概述
在網上講差分陣列的博文很少,也很難找到。一度以為差分陣列是傳播於小眾的神犇技巧所以一直放著沒有去研習。今天做了 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 ...