zoukankan      html  css  js  c++  java
  • AT2274 [ARC066D] Contest with Drinks Hard

    先考虑不修改怎么做,可以令 (dp_i) 表示前 (i) 个题能获得的最大得分。那么我们有转移:

    [dp_i = min{dp_{i - 1}, dp_{j} + frac{(i - j + 1)(i - j)}{2} - (S_i - S_j)}(0 le j < i) ]

    很显然后面那部分是一个斜优的形式,我们化一下柿子可以得到:

    [dp_j + frac{j(j - 1)}{2} + S_j = ij + dp_i + S_i - frac{i(i + 1)}{2} ]

    可以发现我们需要维护一个上凸包的形式,每次查询的斜率单调递增,加入凸包的点单调递增,可以使用单调栈完成这个操作。

    接下来再考虑修改,不难发现每次我们修改的这个位置可能是选或不选两种状态,对于后者我们只需维护出 (f_i) 表示 (1 sim i) 这些题能获得的最大得分和 (g_i) 表示 (i sim n) 这些题的最大得分,那么不选这个题的最大得分就是 (f_{i - 1} + g_{i + 1})

    接下来我们考虑选择这个位置的情况。令 (h_i) 表示强制选择 (i) 这个位置的最大得分,考虑枚举左边第一个没有选择的位置 (l),和右边第一个选择的位置 (r)(这样好算一些),那么有转移:

    [h_i = min{f_l + g_{r + 1} + frac{(r - l + 1)(r - l)}{2} - (S_r - S_l)}(0 le l < i, i le r le n) ]

    根据这个柿子我们可以发现这样一个事实,如果我们每次修改了一个 (T_i)(S_l, f_l, g_{r + 1}) 是不会有影响的,对 (S_r) 相当于整体加上了一个变化量,因此我们可以提前预处理出最开始序列的 (h_i) 那么修改某个点后必选这个位置的最大得分就是 (h_i + Delta) 了。

    继续考虑上面那个式子,我们化简一下式子看看这个式子还能不能斜率优化:

    [g_{r + 1} + frac{r(r + 1)}{2} - S_r = lr + h_i - f_l - frac{l(l - 1)}{2} - S_l ]

    可以发现我们在枚举 (i) 同时枚举 (l),那么上面的那个式子相当于也是一个斜率优化的形式,这样我们就可以做到 (O(n ^ 2)) 了,但这和暴力重构的复杂度是一样的,我们还需另辟蹊径。

    反过来想,我们不枚举每个 (i),而是直接考虑 (l, r) 对中间每个 (i) 的贡献。可以发现这样的 ((l, r)) 其实是一个点对的形式,于是我们可以考虑使用 ( m CDQ) 分治来优化这个过程。

    具体来讲,假设当前分治考虑的区间为 ([L, R]),那么我们当前值考虑 (l)([L, Mid])(r)([Mid + 1, R]) 的点对 ((l, R)),这样显然是能包含所有点对的。根据上面那个式子,我们依然需要枚举每个 (l),只是我们惊奇地发现我们的右端点的范围其实已经确定,那么每次在枚举 (l) 之前我们先将右半边区间的所有 (r) 都插入到上凸包中,那么我们从左往右地去枚举每个 (l),斜率单调递增,依然可以直接使用单调栈解决。不难发现我们这里求出的每个 (l) 的答案可以对 ([l + 1, Mid]) 中的所有 (h_i) 造成贡献,反过来每个 (i) 可以被 ([L - 1, i - 1])(l) 造成贡献,于是我们只需要记录一个前缀 (max) 就可以了。但这样我们只能计算对 ([L, Mid]) 中的点的贡献,那么对 ([Mid + 1, R]) 中点的贡献怎么算呢?我们只需要将这个区间反过来,将 (r) 看作 (l)(l) 看作 (r) 做一遍一样的流程即可。

    注意因为我们要保证斜率优化的决策单调性,因此我们每次枚举的斜率 (l) 要单调递增,每次插入凸包中的点 (i) 横坐标即下边也要单调递增,这意味着我们都要从小到大地去枚举。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 300000 + 5
    #define int long long
    #define mid (l + r) / 2
    #define inf 10000000000000000
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    #define dep(i, l, r) for(int i = r; i >= l; --i)
    typedef double D;
    int n, m, P, X, top, f[N], g[N], h[N], t[N], T[N], S1[N], S2[N], st[N], mx[N];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int Y1(int x){
        return f[x] + x * (x - 1) / 2 + S1[x];
    }
    int Y2(int x){
        return g[x] + x * (x - 1) / 2 + S2[x];
    }
    int Y3(int x){
        return x * (x + 1) / 2 - S1[x] + g[x + 1];
    }
    int Y4(int x){
        return x * (x - 1) / 2 + S1[x] + f[x];
    }
    D slope1(int a, int b){
        return 1.0 * (Y1(a) - Y1(b)) / (a - b);
    }
    D slope2(int a, int b){
        return 1.0 * (Y2(a) - Y2(b)) / (a - b);
    }
    D slope3(int a, int b){
        return 1.0 * (Y3(a) - Y3(b)) / (a - b);
    }
    D slope4(int a, int b){
        return 1.0 * (Y4(a) - Y4(b)) / (a - b);
    }
    void solve(int l, int r){
        if(l == r){ h[l] = max(h[l], f[l - 1] + g[l + 1] + 1 - T[l]); return;}
        solve(l, mid), solve(mid + 1, r);
        st[top = 1] = mid + 1;
        rep(i, mid + 2, r){
            while(top > 1 && slope3(st[top], st[top - 1]) < slope3(st[top - 1], i)) --top;
            st[++top] = i;
        }
        rep(i, l - 1, mid - 1){
            while(top > 1 && i > slope3(st[top], st[top - 1])) --top;
            t[i] = f[i] + (st[top] - i + 1) * (st[top] - i) / 2 + g[st[top] + 1] - S1[st[top]] + S1[i];
        }
        mx[l - 1] = -inf;
        rep(i, l, mid) mx[i] = max(mx[i - 1], t[i - 1]), h[i] = max(h[i], mx[i]);
        st[top = 1] = l - 1;
        rep(i, l, mid - 1){
            while(top > 1 && slope4(st[top], st[top - 1]) < slope4(st[top - 1], i)) --top;
            st[++top] = i;
        }
        rep(i, mid + 1, r){
            while(top > 1 && i > slope4(st[top], st[top - 1])) --top;
            t[i] = g[i + 1] + (i - st[top] + 1) * (i - st[top]) / 2 + f[st[top]] - S1[i] + S1[st[top]];
        }
        mx[r + 1] = -inf;
        dep(i, mid + 1, r) mx[i] = max(mx[i + 1], t[i]), h[i] = max(h[i], mx[i]);
    }
    signed main(){
        n = read();
        rep(i, 1, n) T[i] = read(), S1[i] = S1[i - 1] + T[i], h[i] = -inf;
        dep(i, 1, n) S2[n - i + 1] = S2[n - i] + T[i];
        st[++top] = 0;
        rep(i, 1, n){
            while(top > 1 && i > slope1(st[top], st[top - 1])) --top;
            f[i] = max(f[i - 1], f[st[top]] + (i - st[top] + 1) * (i - st[top]) / 2 + S1[st[top]] - S1[i]);
            while(top > 1 && slope1(st[top], st[top - 1]) < slope1(st[top - 1], i)) --top;
            st[++top] = i;
        }
        st[top = 1] = 0;
        rep(i, 1, n){
            while(top > 1 && i > slope2(st[top], st[top - 1])) --top;
            g[i] = max(g[i - 1], g[st[top]] + (i - st[top] + 1) * (i - st[top]) / 2 + S2[st[top]] - S2[i]);
            while(top > 1 && slope2(st[top], st[top - 1]) < slope2(st[top - 1], i)) --top;
            st[++top] = i;
        }
        reverse(g + 1, g + n + 1);
        solve(1, n);
        m = read();
        while(m--) P = read(), X = read(), printf("%lld
    ", max(f[P - 1] + g[P + 1], h[P] + T[P] - X));
        return 0;
    }
    
    GO!
  • 相关阅读:
    78. Subsets
    93. Restore IP Addresses
    71. Simplify Path
    82. Remove Duplicates from Sorted List II
    95. Unique Binary Search Trees II
    96. Unique Binary Search Trees
    312. Burst Balloons
    程序员社交平台
    APP Store开发指南
    iOS框架搭建(MVC,自定义TabBar)--微博搭建为例
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13456995.html
Copyright © 2011-2022 走看看