Wannafly挑战赛18 E.极差 线段树+单调栈

原题链接:https://ac.nowcoder.com/acm/contest/129/E

题意

给出三个长度为n的序列,一个区间[l,r]的价值定义为三个区间内最大和最小值之差的乘积,求区间和,对2^32取模。

分析

对于最大最小值之差的问题,我们可以枚举右断点,左端点利用单调栈进行维护。

需要开两个单调栈,一个维护最大值区间,一个维护最小值区间。

对于维护最小值的单调栈,栈内元素应该单调递增,因为右端点确定的情况下,如果是左边存在比当前元素大的值,那么那个值对右边的最小值已经不会有贡献了(因为如果要取最小值也是当前值),因此将那些大于当前值得栈顶元素弹出,并加回那段区间元素值,直到满足当前值大于栈顶元素值,将当前元素进栈,并对区间重新计算贡献。(对于最小值应该减去当前权值,对于最大值应该加上当前权值)。

这样对于单个序列的贡献就可以求出来了,那怎么维护三个序列。

我们把三个序列的贡献看成 a ∗ b ∗ c a*b*c abc

如果a增加x,那么会影响到和a有关的所有值,如a,ab,ac,abc

那么我们维护1,a,b,c,ab,ac,bc,abc的8棵线段树,如果有k个序列,其实就是2^k棵线段树,然后如果给a加上x,那么a加上x,ab加上bx,ac加上cx,abc加上bcx。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<ll, ll> PII;
const ll inf = 1e18;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\n'

namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0; T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}
namespace Comb {
    ll f[N];
    ll ksm(ll a, ll b) {
        ll res = 1, base = a;
        while (b) {
            if (b & 1) res = res * base % mod;
            base = base * base % mod;
            b >>= 1;
        }
        return res;
    }
    void init() {
        f[0] = 1;
        for (ll i = 1; i < N; i++) f[i] = f[i - 1] * i % mod;
    }
    ll C(ll a, ll b) {
        if (a < 0 || b < 0 || b > a) return 0;
        return f[a] * ksm(f[a - b], mod - 2) % mod * ksm(f[b], mod - 2) % mod;
    }
}
namespace Seg {
#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
    struct node {
        ul sum[8], tag[3];
    }tr[N<<2];
    void push_up(int u) {
        for (int i = 1; i < 8; i++)
            tr[u].sum[i] = tr[ls].sum[i] + tr[rs].sum[i];
    }
    void gao(int u, int x, ul val) {
        int state = 1 << x;
        for (int i = 1; i < 8; i++) {
            if ((i & state) == state)
                tr[u].sum[i] += tr[u].sum[i^state] * val;
        }
        tr[u].tag[x] += val;
    }
    void build(int u, int l, int r) {
        tr[u].sum[0] = r - l + 1;
        if (l == r) return;
        build(ls, l, mid);
        build(rs, mid+1, r);
        push_up(u);
    }
    void push_down(int u) {
        for (int i = 0; i < 3; i++) {
            if (tr[u].tag[i]) {
                gao(ls, i, tr[u].tag[i]);
                gao(rs, i, tr[u].tag[i]);
                tr[u].tag[i] = 0;
            }
        }
    }
    void modify(int u, int ql, int qr, int x, int l, int r, ul val) {
        if (ql <= l && qr >= r) {
            gao(u, x, val);
            return;
        }
        push_down(u);
        if (ql <= mid) modify(ls, ql, qr, x, l, mid, val);
        if (qr > mid) modify(rs, ql, qr, x, mid+1, r, val);
        push_up(u);
    }
}


using namespace Seg;
int mi[3][N], ma[3][N], pmi[3], pma[3];
ul b[3][N];

inline void solve() {
    int n; cin >> n;
    ul ans = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> b[i][j];
        }
    }
    build(1, 1, n);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 3; j++) {
            while (pmi[j] && b[j][mi[j][pmi[j]]] > b[j][i]) {
                modify(1, mi[j][pmi[j] - 1] + 1, mi[j][pmi[j]], j, 1, n, b[j][mi[j][pmi[j]]]);
                pmi[j]--;
            }
            mi[j][++pmi[j]] = i;
            modify(1, mi[j][pmi[j] - 1] + 1, i, j, 1, n, -b[j][i]);

            while (pma[j] && b[j][ma[j][pma[j]]] < b[j][i]) {
                modify(1, ma[j][pma[j] - 1] + 1, ma[j][pma[j]], j, 1, n, -b[j][ma[j][pma[j]]]);
                pma[j]--;
            }
            ma[j][++pma[j]] = i;
            modify(1, ma[j][pma[j] - 1] + 1, i, j, 1, n, b[j][i]);
        }
        ans += tr[1].sum[7];
    }
    cout << ans << endl;
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值