关于差分:对于区间修改一般用到差分这一方法。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; }