線分木を使用した複雑な操作の実装

この問題では以下の4つの操作を実装する必要があります:

  • 操作1: 結果にaを加算
  • 操作2: 結果からaを減算
  • 操作3: 結果にaを乗算
  • 操作4: 結果にa * Xを加算

これらの操作を効率的に処理するために、線分木を使用します。線分木は区間最大値と最小値、加算の遅延評価タグ、乗算の遅延評価タグ、代入の遅延評価タグ、および操作4用の遅延評価タグを管理します。

木の構築

通常の線分木の構築を行います。一部の初期化については注釈で説明します。

void build(ll p, ll pl, ll pr) {
    addtag[p] = 0; // グローバル変数はデフォルトで0なので、省略可能
    multag[p] = 1; // 乗算タグは1で初期化
    opt4tag[p] = 0;
    modifytag[p] = 0;
    if (pl == pr) {
        maxtree[p] = mintree[p] = a[pl].val;
        return;
    }
    ll mid = (pl + pr) / 2;
    build(ls(p), pl, mid);
    build(rs(p), mid + 1, pr);
    pushup(p);
}

加算タグの更新

通常の加算タグの更新を行います。

void make_addtag(ll p, ll d) {
    addtag[p] += d;
    maxtree[p] += d;
    mintree[p] += d;
}

乗算タグの更新

乗算タグを更新する際には、加算タグも更新する必要があります。

void make_multag(ll p, ll d) {
    addtag[p] *= d;
    multag[p] *= d;
    opt4tag[p] *= d;
    maxtree[p] *= d;
    mintree[p] *= d;
}

操作4のタグの更新

操作4の要件に従ってタグを更新します。

void make_opt4tag(ll p, ll pl, ll pr, ll d) {
    mintree[p] += a[pl].val * d;
    maxtree[p] += a[pr].val * d;
    opt4tag[p] += d;
}

代入タグの更新

すべてのタグを初期状態に戻します。

void make_modifytag(ll p, ll d) {
    addtag[p] = 0;
    multag[p] = 1;
    opt4tag[p] = 0;
    modifytag[p] = d;
    maxtree[p] = d;
    mintree[p] = d;
}

遅延評価の伝播

全ての遅延評価を統合し、順序を注意深く保つことが重要です。

void pushdown(ll p, ll pl, ll pr) {
    ll mid = (pl + pr) / 2;
    if (modifytag[p]) {
        make_modifytag(ls(p), modifytag[p]);
        make_modifytag(rs(p), modifytag[p]);
        modifytag[p] = 0;
    }
    if (multag[p] != 1) {
        make_multag(ls(p), multag[p]);
        make_multag(rs(p), multag[p]);
        multag[p] = 1;
    }
    if (addtag[p]) {
        make_addtag(ls(p), addtag[p]);
        make_addtag(rs(p), addtag[p]);
        addtag[p] = 0;
    }
    if (opt4tag[p]) {
        make_opt4tag(ls(p), pl, mid, opt4tag[p]);
        make_opt4tag(rs(p), mid + 1, pr, opt4tag[p]);
        opt4tag[p] = 0;
    }
}

区間更新

区間の最小値と最大値の更新を行います。

void min_update(ll p, ll pl, ll pr) {
    if (mintree[p] >= r) {
        make_modifytag(p, r);
        return;
    }
    if (maxtree[p] <= r || pl == pr)
        return;
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    min_update(ls(p), pl, mid);
    min_update(rs(p), mid + 1, pr);
    pushup(p);
}

void max_update(ll p, ll pl, ll pr) {
    if (maxtree[p] <= l) {
        make_modifytag(p, l);
        return;
    }
    if (mintree[p] >= l || pl == pr)
        return;
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    max_update(ls(p), pl, mid);
    max_update(rs(p), mid + 1, pr);
    pushup(p);
}

答えの取得

再帰的に答えを計算します。

void get_ans(ll p, ll pl, ll pr) {
    if (pl == pr) {
        ans[a[pl].idx] = maxtree[p];
        return;
    }
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    get_ans(ls(p), pl, mid);
    get_ans(rs(p), mid + 1, pr);
}

完全なコード

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
using namespace std;

using ll = long long;

struct node {
    ll val, idx;
    bool operator<(const node &k) const {
        return this->val < k.val;
    }
} a[100005];

ll n, l, r, q;
char ch;

ll x[100005], opt[100005], ans[100005], maxtree[400020], mintree[400020], addtag[400020], multag[400020], opt4tag[400020], modifytag[400020];

ll ls(ll p) {
    return p << 1;
}

ll rs(ll p) {
    return p << 1 | 1;
}

void pushup(ll p) {
    maxtree[p] = max(maxtree[ls(p)], maxtree[rs(p)]);
    mintree[p] = min(mintree[ls(p)], mintree[rs(p)]);
}

void build(ll p, ll pl, ll pr) {
    addtag[p] = 0;
    multag[p] = 1;
    opt4tag[p] = 0;
    modifytag[p] = 0;
    if (pl == pr) {
        maxtree[p] = mintree[p] = a[pl].val;
        return;
    }
    ll mid = (pl + pr) / 2;
    build(ls(p), pl, mid);
    build(rs(p), mid + 1, pr);
    pushup(p);
}

void make_addtag(ll p, ll d) {
    addtag[p] += d;
    maxtree[p] += d;
    mintree[p] += d;
}

void make_multag(ll p, ll d) {
    addtag[p] *= d;
    multag[p] *= d;
    opt4tag[p] *= d;
    maxtree[p] *= d;
    mintree[p] *= d;
}

void make_opt4tag(ll p, ll pl, ll pr, ll d) {
    mintree[p] += a[pl].val * d;
    maxtree[p] += a[pr].val * d;
    opt4tag[p] += d;
}

void make_modifytag(ll p, ll d) {
    addtag[p] = 0;
    multag[p] = 1;
    opt4tag[p] = 0;
    modifytag[p] = d;
    maxtree[p] = d;
    mintree[p] = d;
}

void pushdown(ll p, ll pl, ll pr) {
    ll mid = (pl + pr) / 2;
    if (modifytag[p]) {
        make_modifytag(ls(p), modifytag[p]);
        make_modifytag(rs(p), modifytag[p]);
        modifytag[p] = 0;
    }
    if (multag[p] != 1) {
        make_multag(ls(p), multag[p]);
        make_multag(rs(p), multag[p]);
        multag[p] = 1;
    }
    if (addtag[p]) {
        make_addtag(ls(p), addtag[p]);
        make_addtag(rs(p), addtag[p]);
        addtag[p] = 0;
    }
    if (opt4tag[p]) {
        make_opt4tag(ls(p), pl, mid, opt4tag[p]);
        make_opt4tag(rs(p), mid + 1, pr, opt4tag[p]);
        opt4tag[p] = 0;
    }
}

void min_update(ll p, ll pl, ll pr) {
    if (mintree[p] >= r) {
        make_modifytag(p, r);
        return;
    }
    if (maxtree[p] <= r || pl == pr)
        return;
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    min_update(ls(p), pl, mid);
    min_update(rs(p), mid + 1, pr);
    pushup(p);
}

void max_update(ll p, ll pl, ll pr) {
    if (maxtree[p] <= l) {
        make_modifytag(p, l);
        return;
    }
    if (mintree[p] >= l || pl == pr)
        return;
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    max_update(ls(p), pl, mid);
    max_update(rs(p), mid + 1, pr);
    pushup(p);
}

void get_ans(ll p, ll pl, ll pr) {
    if (pl == pr) {
        ans[a[pl].idx] = maxtree[p];
        return;
    }
    pushdown(p, pl, pr);
    ll mid = (pl + pr) / 2;
    get_ans(ls(p), pl, mid);
    get_ans(rs(p), mid + 1, pr);
}

int main() {
    freopen("std.in", "r", stdin);
    freopen("std.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    cin >> n >> l >> r;
    for (ll i = 1; i <= n; i++) {
        cin >> ch >> x[i];
        if (ch == '+')
            opt[i] = 1;
        else if (ch == '-')
            opt[i] = 2;
        else if (ch == '*')
            opt[i] = 3;
        else
            opt[i] = 4;
    }
    cin >> q;
    for (ll i = 1; i <= q; i++)
        cin >> a[i].val, a[i].idx = i;
    sort(a + 1, a + q + 1);
    build(1, 1, q);
    for (ll i = 1; i <= n; i++) {
        if (opt[i] == 1)
            make_addtag(1, x[i]);
        else if (opt[i] == 2)
            make_addtag(1, -x[i]);
        else if (opt[i] == 3)
            make_multag(1, x[i]);
        else
            make_opt4tag(1, 1, q, x[i]);
        min_update(1, 1, q);
        max_update(1, 1, q);
    }
    get_ans(1, 1, q);
    for (int i = 1; i <= q; i++)
        cout << ans[i] << endl;
    return 0;
}

タグ: C++ 線分木 遅延評価 区間クエリ

6月12日 18:13 投稿