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!
  • 相关阅读:
    WP7应用开发笔记插曲 小心使用MessageBox
    WP7应用开发笔记 继承BitmapSource并使用独立存储来缓存远程的图片
    WP7应用开发笔记(10) 导航
    Bangumi 番組計劃 WP手机客户端发布
    WP7应用开发笔记(17) 提交应用
    WP7应用开发笔记(8) IP输入框控件
    从FLC中学习的设计模式系列结构型模式(2)装饰
    Windows Phone Toolkit for WP8 已经出了
    狂神说HTML笔记
    期待
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13456995.html
Copyright © 2011-2022 走看看