【树状数组】牛客练习赛52 B.Galahad

发布时间 2023-08-26 18:30:01作者: blockche

【树状数组】牛客练习赛52 B.Galahad

题目链接:https://ac.nowcoder.com/acm/contest/1084/B

题意

多组询问,计算区间和,但相同的数只能被计算一次。

题解

  1. 离线处理询问,按右端点排序。
  2. 对于相同的数字,我们只能选一个加入到区间和中,我们是枚举右端点,所以选择最靠近右端点左边的数字最优。所以每次在树状数组加入当前数字时,在上一次加入这个数字的位置把它删掉。具体见代码。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

template <typename T>
struct Fenwick {
    int n;
    vector<T> a;
    Fenwick(int n = 0) : n(n), a(n, T()) {}
    void add(int x, T d) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] += d;
        }
    }
    T sum(int x) {
        auto ans = T();
        for (int i = x; i >= 1; i -= i & -i) {
            ans += a[i - 1];
        }
        return ans;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<vector<pair<int, int>>> qry(n);
    for (int i = 0; i < m; i++) {
        int l, r;
        cin >> l >> r;
        l--, r--;
        qry[r].push_back({l, i});
    }

    int mx = *max_element(a.begin(), a.end());
    vector<int> vis(mx + 1, -1);

    vector<i64> ans(m);
    Fenwick<i64> fen(n + 2);
    for (int i = 0; i < n; i++) {
        if (vis[a[i]] != -1) {
            fen.add(vis[a[i]], -a[i]);
        }
        fen.add(i, a[i]);
        vis[a[i]] = i;

        for (auto [l, id] : qry[i]) {
            ans[id] = fen.sum(i + 1) - fen.sum(l);
        }
    }

    for (int i = 0; i < m; i++) {
        cout << ans[i] << '\n';
    }

    return 0;
}

末尾

把加入树状数组的数字改为加入1,即可求区间出现的数的种类。

即:

Fenwick<i64> fen(n + 2);
for (int i = 0; i < n; i++) {
    if (vis[a[i]] != -1) {
        fen.add(vis[a[i]], -1); //this
    }
    fen.add(i, 1); //this
    vis[a[i]] = i;
    for (auto [l, id] : qry[i]) {
        ans[id] = fen.sum(i + 1) - fen.sum(l);
    }
}