【NOIP2017提高A组冲刺11.4】Genocide

本文探讨了一种复杂的区间问题,通过将问题分为50分和100分两种难度进行解析。首先介绍了如何利用单调栈解决基础问题,并进一步提出了采用分治策略处理更复杂情况的方法。文章详细阐述了具体的实现思路和步骤。

Description:

这里写图片描述

这里写图片描述

题解:

先思考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<cstdio>
#include<algorithm>
#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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值