【树状数组】牛客练习赛52 B.Galahad
【树状数组】牛客练习赛52 B.Galahad
题目链接:https://ac.nowcoder.com/acm/contest/1084/B
题意
多组询问,计算区间和,但相同的数只能被计算一次。
题解
- 离线处理询问,按右端点排序。
- 对于相同的数字,我们只能选一个加入到区间和中,我们是枚举右端点,所以选择最靠近右端点左边的数字最优。所以每次在树状数组加入当前数字时,在上一次加入这个数字的位置把它删掉。具体见代码。
代码
#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);
}
}