【NOIP2017提高A组冲刺11.4】Genocide

Description:

【NOIP2017提高A组冲刺11.4】Genocide_第1张图片

这里写图片描述

题解:

先思考50分的做法。
显然是拆式子,把和i有关的划到一边,和j有关的划到一边,中间有个-ij,维护一个交点递减的单调栈,直接做就好了(斜率优化什么的我不会)。

100分的做法比较坑爹。
对于x,首先预处理不选它的最大值,用50分的做法前后做两遍就行了。

还要求出一定选它的最大值,这就比较玄乎了。

关于区间的,可以yy出分治。

对于区间[x..y],设 m = x +y/ 2

可以对[m+1..y]先做出单调栈。
接着顺序地从x到m扫,维护前缀max,这样可以求出x..m经过这些点并且经过m的区间的最大值并打在上面,发现对于[m+1..y]的还没有做,此时倒着再扫一遍即可。

接着分治,感觉十分的巧,不管怎样,就是这么做了。

Code:

#include
#include
#define ll long long
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
#define fd(i, x, y) for(ll i = x; i >= y; i --)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const ll N = 6e5 + 5;

ll n, m, x, y;
ll a[N], s[N], f[N], z[N], d[N], c[N], g[N], z0;

double ji(ll x, ll y, ll u, ll v) {
    return (double)(y - v) / (double)(u - x);
}

double jx(ll x) {
    return ji(-z[x - 1], d[z[x - 1]], -z[x], d[z[x]]);
}

void Solve(ll *f) {
    z0 = 0;
    fo(i, 1, n) s[i] = s[i - 1] + a[i];
    f[1] = max(0, 1 - a[1]); d[1] = f[1] + s[1]; z[++ z0] = 1;
    fo(i, 2, n) {
        f[i] = f[i - 1];
        f[i] = max(f[i], i * (i + 1) / 2 - s[i]);
        ll ans = 1;
        for(ll l = 2, r = z0; l <= r; ) {
            ll m = l + r >> 1;
            if(jx(m) >= i)
                ans = m, l = m + 1; else r = m - 1;
        }
        f[i] = max(f[i], d[z[ans]] - z[ans] * i - s[i] + i * (i + 1) / 2);
        d[i] = f[i] + s[i] + i * (i - 1) / 2;
        while(z0 > 1 && jx(z0) < ji(-z[z0], d[z[z0]], -i, d[i])) z0 --;
        z[++ z0] = i;
    }
}

void dg(int x, int y) {
    if(x == y) return;
    int m = x + y >> 1;
    z0 = 1; z[1] = m + 1;
    d[m + 1] = g[m + 2] - s[m + 1] + (m + 1) * (m + 2) / 2;
    fo(i, m + 2, y) {
        d[i] = g[i + 1] - s[i] + i * (i + 1) / 2;
        while(z0 > 1 && jx(z0) < ji(-z[z0], d[z[z0]], -i, d[i])) z0 --;
        z[++ z0] = i;
    }
    ll mx = -1e18; int r = z0;
    fo(i, x - 1, m) {
        c[i] = max(c[i], mx);
        while(r > 1 && d[z[r]] - i * z[r] < d[z[r - 1]] - i * z[r - 1]) r --;
        mx = max(mx, f[i] + s[i] - i * (i - 1) / 2 - i * z[r] + d[z[r]]);
    }
    z0 = 1; z[1] = x - 1;
    d[x - 1] = f[x - 1] + s[x - 1] - (x - 1) * (x - 2) / 2;
    fo(i, x, m) {
        d[i] = f[i] + s[i] - i * (i - 1) / 2;
        while(z0 > 1 && jx(z0) < ji(-z[z0], d[z[z0]], -i, d[i])) z0 --;
        z[++ z0] = i;
    }
    mx = -1e18; r = 1;
    fd(i, y, m + 1) {
        while(r < z0 && d[z[r]] - z[r] * i < d[z[r + 1]] - z[r + 1] * i) r ++;
        mx = max(mx, d[z[r]] - z[r] * i + g[i + 1] - s[i] + i * (i + 1) / 2);
        c[i] = max(c[i], mx);
    }
    dg(x, m); dg(m + 1, y);
}

int main() {
    freopen("genocide.in", "r", stdin);
    freopen("genocide.out", "w", stdout);
    scanf("%lld", &n);
    fo(i, 1, n) scanf("%lld", &a[i]);
    Solve(f);
    fo(i, 1, n / 2) swap(a[i], a[n - i + 1]);
    Solve(g);
    fo(i, 1, n / 2) swap(g[i], g[n - i + 1]);
    fo(i, 1, n / 2) swap(a[i], a[n - i + 1]);
    fo(i, 1, n) s[i] = s[i - 1] + a[i];
    fo(i, 1, n) c[i] = 1 - a[i];
    dg(1, n);
    for(scanf("%lld", &m); m; m --) {
        scanf("%lld %lld", &x, &y);
        printf("%lld\n", max(f[x - 1] + g[x + 1], c[x] + a[x] - y));
    }
}

你可能感兴趣的:(动态规划,单调队列,&&,单调栈,分治,斜率优化)