關於差分:對於區間修改一般用到差分這一方法。git
方法是 創建一個差分數組\(a\),也就是原數列後一位減前一位的值\((a[i] = a[i] - a[i - 1])\) 。數組
當某一個區間加上一個值\(w\)以後,該區間的左端點就會比左端點減一的值多\(w\),而右端點加一就會比右端點的值少\(w\)。數據結構
因此對於修改的區間\((l,r)\),\(a[l] += w, a[r + 1] -= w\); 時間複雜度爲\(O(1)\),比數據結構優。spa
查詢值的時候,只須要將差分數組作一遍前綴和便可。
原理是 當\(a[l]\)加上\(w\)時,作前綴和的時候從l一直到數列最後都會受到加\(w\)的影響,
由於會影響區間外的值,因此在右端點加\(1\)處減去\(w\),來消除從右端點加一到數列最後的影響。code
時間複雜度\(O(n)\),比線段樹等數據結構慢不少
因此通常當只查詢最後結果時,用差分這一方法比較優。get
關於本題:能夠很天然的想到差分這一方法。
可是區間修改時不是加上或減去同一個值,而是一個等差數列it
怎麼辦呢?io
考慮對於一個等差數列,它的差分數組很顯然都是同一個值
而對於一個區間,每一個點要加的值又是一個等差數列
因此須要知道 等差數列的差分數組 來統計 區間每一個點要加的值 的 差分數組class
怎樣獲得等差數列的差分數組\(sumcha\)呢?(爲了方便理解,數組名稱使用代碼中的變量)變量
考慮再進行一次差分,統計\(sumcha\)的差分數組\(chacha\),很顯然只須要修改區間左右端點便可
這是由於 \(sumcha\)每一個值都相同,因此只須要在\(chacha\)開頭加上公差,結尾加一處減去公差,作前綴和就能夠獲得\(sumcha\)
因此對這個數組作\(1-n\)的前綴和能夠獲得\(sumcha\),這個就是整個數列的差分數組
而後再對整個序列的差分數組作前綴和,就能夠獲得每一個點修改的值。
例如:對於\(0\ 0\ 0\ 0\ 0\)這個序列,執行\((1\ 5\ 2\ 10)\)這個操做
\(chacha[1] += 2, chacha[6] -= 2\)
作前綴和能夠獲得:\(2\ 2\ 2\ 2\ 2\)
這很顯然就是\(2\ 4\ 6\ 8\ 10\)的差分數組
再對它作前綴和,就能夠獲得:\(2\ 4\ 6\ 8\ 10\)
也就是每一個點要加的值
注意:
代碼:
#include <cstdio> #include <cctype> typedef long long ll; const int _ = 10000001; ll sum_ans[_], cha_cha[_], sum_cha[_]; inline ll max(ll a, ll b) { return a > b ? a : b; } inline ll read() { ll s = 1, w = 0; char ch = getchar(); for(; ! isdigit(ch); ch = getchar()) if(ch == '-') s = -1; for(; isdigit(ch); ch = getchar()) w = w * 10 + ch - '0'; return s * w; } int main() { int n = read(), m = read(), l, r; ll s, e, cha; while (m --) { l = read(), r = read(); s = read(), e = read(), cha; cha = (e - s) / (r - l);//計算公差 cha_cha[l] += cha, cha_cha[r + 1] -= cha; //等差數列的差分的差分= = sum_ans[l] += s - cha, sum_ans[r + 1] -= s - cha; //當等差數列開頭不爲公差時 sum_ans[r + 1] -= (r - l + 1) * cha;//等差數列差分 } ll Max = 0, ans = 0; for (register int i = 1; i <= n; i ++) { sum_cha[i] = sum_cha[i - 1] + cha_cha[i]; //前綴和求等差數列的差分數組 sum_ans[i] += sum_ans[i - 1] + sum_cha[i];//前綴和求每一個點要加的值 ans ^= sum_ans[i], Max = max(Max, sum_ans[i]); } printf("%lld %lld", ans, Max); return 0; }