Three Sequences(差分序列构造)
题目链接:D. Three Sequences
题意:给你一个长度为n的序列a,你要把每一个ai拆成bi + ci,形成两个长度为n的序列b和c,并且要保证b序列单调非递减,c序列单调非递增,然后进行m次操作,每次操作使得a序列的[l, r]区间增加k,问每次操作之后b,c序列的最大值最小是多少
Input
第一行输入一个n(1 <= n <= 1e5),代表有n个数,第二行输入n个数表示a序列(-1e9 <= ai <= 1e9),第三行输入m(1 <= m <= 1e5)表示有m次操作,然后m行,每行输入l, r, k(1 <= l <= r <= n, -1e9 <= k <= 1e9)表示a序列[l, r]区间增加k。
Output
首先输出未操作之前的b,c序列的最大值最小是多少,然后每次操作之后输出b,c序列的最大值最小是多少。
Sample Input
4
2 -1 7 3
2
2 4 -3
3 4 2
Sample Output
5
5
6
思路:
由于b序列是非递减的,c序列是非递增的,那么题目实际上让我们求的就是max(bn, c1)的最小值。
直接构造b,c序列无从下手,那么我们考虑构造b,c序列的差分数组,也就是把a的差分数组拆成b,c的差分数组。记a的差分数组为da。
考虑到b数组要非递增,c数组要非递减,所以b数组差分数组中的每个值都>=0,c数组差分数组中的每个值都<=0,那么对于dai,如果dai>0那么他一定要全都分到b的差分数组中,否则会使bn更大,如果dai<0那么他一定要全都分到c的差分数组中,否则也会使bn更大。所以,对于a的差分数组,正差分一定要分到b的差分数组中,负差分一定要分到c的差分数组中。
差分序列确定后,max(bn, c1)的值只由b1,c1决定,记a差分数组的正差分之和为sum。
那么bn = b1 + sum, 要使得max(bn, c1)最小,那么需要使bn = c1,即b1 + sum = c1,又b1 + c1 = a1,所以b1 = (a1 - sum) / 2,c1 = a1 - b1,答案为max(b1 + sum, c1)
所以最终答案只与b1,c1,sum有关,因此下面进行区间操作时,对差分序列只改变了两个值,对sum做维护即可O(1)出答案。

#include<bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int N = 100010; int n, m; ll a[N], b[N], k; int main() { int l, r; scanf("%d", &n); ll sum = 0; for(int i = 1; i <= n; i++) { scanf("%lld", &a[i]); b[i] = a[i] - a[i - 1]; if(b[i] > 0 && i > 1) sum += b[i]; } ll x = (b[1] - sum) / 2; ll y = b[1] - x; ll ans = max(x + sum, y); printf("%lld\n", ans); //cout << "++" << sum << endl; scanf("%d", &m); for(int i = 1; i <= m; i++) { scanf("%d %d %lld", &l, &r, &k); ll now1 = b[l] + k; if(l != 1) { if(b[l] > 0 && now1 <= 0) { sum -= b[l]; } else if(b[l] <= 0 && now1 > 0) { sum += now1; } else if(b[l] >= 0 && now1 >= 0) { sum += k; } } b[l] = now1; ll now2 = b[r + 1] - k; if(r != n) { if(b[r + 1] > 0 && now2 <= 0) { sum -= b[r + 1]; } else if(b[r + 1] <= 0 && now2 > 0) { sum += now2; } else if(b[r + 1] >= 0 && now2 >= 0) { sum -= k; } } b[r + 1] = now2; //cout << sum << "---" << endl; x = (b[1] - sum) / 2; y = b[1] - x; ans = max(x + sum, y); printf("%lld\n", ans); } return 0; }