维护集合最值和集合全体加

你有一个可重集合初始有n个数,有4种操作,$5e5次操作$

  • 向集合中加入一个数
  • 删除集合中一个数
  • 集合中所有数加x
  • 查询集合最大值

进一步考虑另一个可以使用这个模型的问题。一个vector,动态在尾部增加数,删除数,边操作边求后缀和最小值

#include <bits/stdc++.h>
#ifdef LOCAL
#include "debug.h"
#else
#define deb(...)
#endif
using namespace std;
#define ll long long
// #define int long long
#define ull unsigned long long
#define pii pair<int, int>
#define db double
#define baoliu(x, y) cout << fixed << setprecision(y) << x
#define endl "\n"
#define alls(x) (x).begin(), (x).end()
#define fs first
#define sec second
#define bug(x) cerr << #x << " = " << x << endl
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
const double PI = acos(-1.0);
void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    vector<int> suf(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = n; i >= 1; i--) {
        if (i == n)
            suf[i] = a[i];
        else
            suf[i] = suf[i + 1] + a[i];
    }
    multiset<int> st;
    for (int i = 1; i <= n; i++) st.insert(suf[i]);
    int offset = 0;
    int sufsum = suf[1];
    // 集合所有数加上x
    auto update = [&](int x) {
        offset += x;
    };
    // 1-加入一个数到集合
    auto add = [&](int x) {
        st.insert(x - offset);
    };
    // 2-删除集合里的数,保证存在
    auto del = [&](int x) {
        int cur = x - offset;
        assert(st.find(cur) != st.end());
        st.erase(st.find(cur));
    };
    // 3-查询后缀和最大值
    auto query = [&] {
        return *prev(st.end()) + offset;
    };
    int m;
    cin >> m;
    for (int i = 1; i <= m; i++) {
        int op;
        cin >> op;
        if (op == 1) {
            int x;
            cin >> x;
            sufsum += x;
            a.push_back(x);
            update(x);
            add(sufsum);
        } else if (op == 2) {
            int x = a.back();
            a.pop_back();
            del(sufsum);
            sufsum -= x;
            update(-x);
        } else
            cout << query() << endl;
    }
}

signed main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
#ifdef LOCAL
    double starttime = clock();
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
#endif
    int t = 1;
    // cin >> t;
    while (t--) solve();
#ifdef LOCAL
    double endtime = clock();
    cerr << "Time Used: " << (double)(endtime - starttime) / CLOCKS_PER_SEC * 1000 << " ms" << endl;
#endif
    return 0;
}

怎么转化的?

假设初始有n个元素 我就把n个后缀和存进集合里。需要维护如下操作:

  • 集合删去一个数,并且集合中每个数减去这个数

  • 集合加入一个数,并且集合中每个数加上这个数

  • 求集合最小值

    开一个vector按照题意模拟就可以知道每次最后一个数是谁,维护一个sufsum就可以知道当前后缀和是多少,用multiset就可以知道集合最小值。

    • 唯一要解决的问题就是集合一起加一个数怎么维护