935F - Fafa and Array 线段树 分类讨论

题目链接:http://codeforces.com/contest/935/problem/F

题意

有一个长度为n的数组A,定义函数F(A)为

现在有q次操作。

1.l r x代表输出如果在[l, r] 中找一个数加上x之后的F(A)的最大值。

2.l r x代表[l, r] 区间增加x。

题解

我们用d[i]记录a[i + 1] - a[i] ,ans代表当前的F(A)。

对于操作1,有这么几种情况:

1. [l, r] 中存在一个数 a[i] , 使得a[i - 1] < a[i] , a[i + 1] < a[i],这时x加在a[i]上面的收益可以达到 2x。

2. [l, r] 中存在a[i - 1] < a[i] , a[i + 1]  > a[i] 或者 a[i - 1] > a[i] ,a[i + 1] < a[i]。我们假设a[i + 1] > a[i],则收益也有两种情况:

① d[i] >  x : 收益为 x + a[i + 1] - a[i] - (a[i + 1] - a[i] + x)  =  0, 区间长度大于等于2时,一定存在这种情况。

② d[i] < x : 收益为 x +  (x - (a[i + 1]  - a[i]))  - (a[i + 1] - a[i])  = 2 * x - 2 * d[i]。

3. [l, r]中存在 a[i - 1] > a[i] , a[i + 1] > a[i], 这种情况的收益有4种情况。

① d[i] > x && -d[i - 1] > x  : 这种情况收益肯定最小,只有可能l = r 的时候存在, 特判一下。

② d[i] < x && -d[i - 1] > x  : 这时的收益是 x - (a[i + 1] - a[i]) - (a[i + 1] - a[i])  +  (a[i - 1] - a[i] - x) - (a[i - 1] - a[i]) =  - 2  * d[i]。

③ d[i] > x && -d[i - 1] < x  : 与上同理

④ d[i] < x && -d[i - 1] < x  : 2 * x - 2 * d[i] - 2 * -d[i -1]。

至于操作2,区间更新其实只改变了d[l - 1] 和d[r],单独更新一下同时维护ans即可。

 

综合一下:我们线段树里维护 min(d[i - 1], 0) +  min(-d[i], 0)  的最大值。

如果l != r ,那么答案就是max(ans + 2 * query(l, r), 0)。

如果l = r ,答案直接计算即可。

此时全部情况皆被综合了。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <unordered_set>
#include <cmath>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 2e5 + 5;
const int mod = 1e9 + 7;

int a[MAXN];
ll d[MAXN];

ll tree[MAXN * 4];

ll query (int id, int l, int r, int L, int R) {
    if (L <= l && R >= r) {
        return tree[id];
    }
    int mid = (l + r) >> 1;
    ll ret = -INFLL;
    if (L <= mid) ret = max (ret, query (lson, L, R) );
    if (R > mid) ret = max (ret, query (rson, L, R) );
    return ret;
}

void update (int id, int l, int r, int x, ll v) {
    if (l == r) {
        tree[id] = v;
        return ;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) update (lson, x, v);
    else update (rson, x, v);
    tree[id] = max (tree[id << 1], tree[id << 1 | 1]);
}



int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int n;
    cin >> n;
    ll ans = 0;
    for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);
    for (int i = 1; i < n; i++) {
        d[i] = a[i + 1] - a[i];
        ans = (ans + abs(d[i]));
    }
    for (int i = 1; i <= n; i++) update (1, 1, n, i, min (d[i-1], 0LL) + min (-d[i], 0LL) );
    int q;
    cin >> q;
    debug(ans)
    while (q--) {
        int op, l, r, x;
        scanf ("%d %d %d %d", &op, &l, &r, &x);
        if (op == 1) {
            if (l == r) printf ("%lld\n", ans + (l == 1 ? 0 : abs (d[l - 1] + x)- abs (d[l - 1]))
                        + (l == n ? 0 : -abs (d[l]) + abs (d[l] - x)) );
            else {
                printf("%lld\n", ans + max(2 * (x + query(1, 1, n, l, r)), 0LL));
                debug((x + query(1, 1, n, l, r)))
            }
        } else {
           if(l > 1) {
                ans -= abs(d[l - 1]);
                d[l - 1] += x;
                update(1, 1, n, l - 1, min(d[l - 2], 0LL) + min(-d[l-1], 0LL));
                update(1, 1, n, l,  min(d[l - 1], 0LL) + min(-d[l], 0LL));
                ans += abs(d[l - 1]);
           }
           if(r < n) {
                ans -= abs(d[r]);
                d[r] -= x;
                update(1, 1, n, r, min(d[r-1], 0LL) + min(-d[r], 0LL));
                update(1, 1, n, r + 1, min(d[r], 0LL) + min(-d[r + 1], 0LL));
                ans += abs(d[r]);
           }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值