【线段树 经典技巧】10.7序列绝对值
依旧是经典的线段树处理新定义权值以及拆绝对值技巧
不过稍微有些细节
题目大意
初始先给定一个序列$A$,定义一个序列的绝对权值为$\sum\limits_{i=2}^n|a_i-a_{i-1}|$.
现在有$q$次操作,每个操作或是询问如果在$A[l,r]$任选一个数加上$x$,$A$的绝对权值最大是多少;或区间加。
$n,q\le 10^5$,保证$1<l\le r<n$.
题目分析
首先这是一个经典的线段树处理新定义权值最值的问题。
考虑一个$i$位置权值为$B$,它的左右权值分别是$A,C$,那么现在如果把它变成数$X$,为答案造成的贡献就是$(|X-A|+|X-C|)-(|B-A|+|B-C|)$.
定义$wrin(i)=(|B-A|+|B-C|)$,处理后贡献式子就是
$=\max\{A+C-2*X,-A-C+2*X,(\max\{A,C\}-\min\{A,C\})\}-wrin(i)$
其中对于每一个位置来说,$A+C$或者$|A-C|$都是方便维护的常量。因此只需要记录区间内$\max\{A+C\},\max\{-A-C\},\max\{|A-C|\}$这三个量就能够处理询问了。
接下来考虑如何修改。
对于修改的区间$[L,R]$,它对上述的三个量都会在$L-1,L,R,R+1$四个单点位置有变化,并且前两个量还有$[L,R]$的整段增量。也就是说要维护区间/单点加和区间最大值的线段树。
还有一个小点就是要处理每次修改后的全局权值,也就是说要询问某一位置元素的权值。这个由于对区间元素的修改是区间的,而查询元素是单点的,所以用树状数组累计一下差分后的数组就好了。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int maxn = 100035; 4 5 int n,q; 6 ll ans,val,a[maxn],num[maxn]; 7 ll abs(ll x){return x > 0?x:-x;} 8 struct SegTree 9 { 10 ll f[maxn<<2],add[maxn<<2]; 11 12 void pushup(int rt) 13 { 14 f[rt] = std::max(f[rt<<1], f[rt<<1|1]); 15 } 16 void pushdown(int rt) 17 { 18 if (add[rt]){ 19 int l = rt<<1, r = rt<<1|1; 20 ll &tag = add[rt]; 21 add[l] += tag, add[r] += tag; 22 f[l] += tag, f[r] += tag, tag = 0; 23 } 24 } 25 void build(int rt, int l, int r, ll c) 26 { 27 if (l==r){ 28 f[rt] = (a[l-1]+a[l+1])*c-abs(a[l]-a[l-1])-abs(a[l]-a[l+1])-2*c*a[l]; 29 if (c==0) f[rt] = abs(a[l+1]-a[l-1])-abs(a[l]-a[l-1])-abs(a[l]-a[l+1]); 30 }else{ 31 int mid = (l+r)>>1; 32 build(rt<<1, l, mid, c); 33 build(rt<<1|1, mid+1, r, c); 34 pushup(rt); 35 } 36 } 37 void modify(int rt, int L, int R, int l, int r, ll c) 38 { 39 if (L <= l&&r <= R){ 40 f[rt] += c, add[rt] += c; 41 }else{ 42 int mid = (l+r)>>1; 43 pushdown(rt); 44 if (L <= mid) modify(rt<<1, L, R, l, mid, c); 45 if (R > mid) modify(rt<<1|1, L, R, mid+1, r, c); 46 pushup(rt); 47 } 48 } 49 void modify(int rt, int l, int r, int pos, ll c) 50 { 51 if (l==r) f[rt] += c; 52 else{ 53 int mid = (l+r)>>1; 54 pushdown(rt); 55 if (pos <= mid) modify(rt<<1, l, mid, pos, c); 56 else modify(rt<<1|1, mid+1, r, pos, c); 57 pushup(rt); 58 } 59 } 60 ll query(int rt, int L, int R, int l, int r) 61 { 62 if (L <= l&&r <= R) return f[rt]; 63 int mid = (l+r)>>1; 64 ll ret = -(1ll<<60); 65 pushdown(rt); 66 if (L <= mid) ret = query(rt<<1, L, R, l, mid); 67 if (R > mid) ret = std::max(ret, query(rt<<1|1, L, R, mid+1, r)); 68 return ret; 69 } 70 }f,g,h; 71 72 int read() 73 { 74 char ch = getchar(); 75 int num = 0, fl = 1; 76 for (; !isdigit(ch); ch=getchar()) 77 if (ch=='-') fl = -1; 78 for (; isdigit(ch); ch=getchar()) 79 num = (num<<1)+(num<<3)+ch-48; 80 return num*fl; 81 } 82 void add(int x, ll c){for (; x<=n; x+=x&-x) num[x] += c;} 83 ll query(int x) //之前这里query类型写成int... 84 { 85 ll ret = 0; 86 for (; x; x-=x&-x) ret += num[x]; 87 return ret; 88 } 89 int main() 90 { 91 n = read(); 92 for (int i=1; i<=n; i++) a[i] = read(); 93 for (int i=1; i<=n; i++) 94 add(i, a[i]-a[i-1]), 95 val += i!=n?abs(a[i]-a[i+1]):0; 96 f.build(1, 1, n, 1); 97 g.build(1, 1, n, -1); 98 h.build(1, 1, n, 0); 99 q = read(); 100 for (int opt,l,r,x; q; --q) 101 { 102 opt = read(), l = read(), r = read(), x = read(); 103 if (opt==1){ 104 ans = std::max(h.query(1, l, r, 1, n), std::max(f.query(1, l, r, 1, n)-2ll*x, g.query(1, l, r, 1, n)+2ll*x))+val; 105 printf("%lld\n",ans); 106 }else{ 107 f.modify(1, l, r, 1, n, -2ll*x); 108 g.modify(1, l, r, 1, n, 2ll*x); 109 f.modify(1, l-1, r-1, 1, n, x); 110 g.modify(1, l+1, r+1, 1, n, -x); 111 f.modify(1, l+1, r+1, 1, n, x); 112 g.modify(1, l-1, r-1, 1, n, -x); 113 ll p1 = query(l-1), p2 = query(l), p3 = query(r), p4 = query(r+1), det = 0; 114 det = abs(p1-p2-x)-abs(p1-p2), val += det; //-----wrin----- 115 f.modify(1, 1, n, l-1, -det); 116 g.modify(1, 1, n, l-1, -det); 117 h.modify(1, 1, n, l-1, -det); 118 f.modify(1, 1, n, l, -det); 119 g.modify(1, 1, n, l, -det); 120 h.modify(1, 1, n, l, -det); 121 det = abs(p4-p3-x)-abs(p4-p3), val += det; 122 f.modify(1, 1, n, r+1, -det); 123 g.modify(1, 1, n, r+1, -det); 124 h.modify(1, 1, n, r+1, -det); 125 f.modify(1, 1, n, r, -det); 126 g.modify(1, 1, n, r, -det); 127 h.modify(1, 1, n, r, -det); //-----wrin----- 128 ll p5 = query(l-2), p6 = query(l+1), p7 = query(r-1), p8 = query(r+2); //容易写成a[..] 129 det = abs(p5-p2)-abs(p5-p2-x); //因为左右两个元素并不知道大小关系 130 h.modify(1, 1, n, l-1, -det); 131 if (l!=r){ 132 det = abs(p1-p6)-abs(p1-p6-x); 133 h.modify(1, 1, n, l, -det); 134 det = abs(p4-p7)-abs(p4-p7-x); 135 h.modify(1, 1, n, r, -det); 136 } 137 det = abs(p8-p3)-abs(p8-p3-x); 138 h.modify(1, 1, n, r+1, -det); 139 add(l, x), add(r+1, -x); 140 } 141 } 142 return 0; 143 }
END