let menu = ["Home", "Algorithms", "CodeHub", "VNOI Statistics"];
Bài này có cách làm thông thường là sửa dụng cây Fenwick:
Duyệt \(i\) từ 1 đến N, ở mỗi bước ta cần đếm số lượng vị trí \(j < i\) có \(L \le S[i] - S[j] \le R\) \(\iff S[i] - R \le S[j] \le S[i] - L\)
Cách làm đơn giản (không AC) sẽ như sau:
Duyệt \(i\) từ 1 đến N:
Dễ thấy cách làm này không thể AC vì giá trị của \(S[i]\) có thể lên đến \(10^{14}\), không thể lưu nổi.
Giải quyết vấn đề này bằng cách rời rạc hóa:
Có thể thấy, cách làm trên khá rườm rà. Bài này có thể sử dụng cây nhị phân tìm kiếm.
Ở mỗi bước chỉ cần duyệt trên cây để đếm số phần tử nằm trong khoảng \([S[i]-R, S[i]-R]\),
sau đó cập nhật thêm \(S[i]\) vào cây. Code treap.cpp
cài đặt phương pháp này sử dụng
cấu trúc Treap, là một loại cây nhị phân tìm kiếm có kết hợp với yếu tố ngẫu nhiên để vừa hiệu
quả vừa dễ cài đặt.
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define LL long long
struct SumIndex: private vector<LL> {
SumIndex(const vector<int> &a) {
set<LL> s;
LL sum = 0;
s.insert(sum);
for (int x: a) sum += x, s.insert(sum);
vector<LL>::operator=(vector<LL>(s.begin(), s.end()));
}
int operator()(LL x) const {
return upper_bound(begin(), end(), x) - begin();
}
int size() const {
return vector::size();
}
};
struct Fenwick {
int n, *f;
Fenwick(int n): n(n), f(new int[n+1]()) {}
~Fenwick() { delete[] f; }
void up(int i) {
for (; i<=n; i += i&-i) f[i]++;
}
int sum(int i) {
int res = 0;
for (; i>0; i -= i&-i) res += f[i];
return res;
}
};
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
LL l, r; cin >> l >> r;
vector<int> a(n);
for (int &x: a) cin >> x;
SumIndex index(a);
LL sum = 0, res = 0;
Fenwick f(index.size());
f.up(index(sum));
for (int x: a) {
sum += x;
res += f.sum(index(sum - l)) - f.sum(index(sum-r-1));
f.up(index(sum));
}
cout << res;
return 0;
}
#include <iostream>
using namespace std;
#define LL long long
struct TreapNode {
LL key;
int prio, cnt = 1;
TreapNode *left = NULL, *right = NULL;
TreapNode(LL key): key(key), prio(rand()) {}
};
int get_cnt(TreapNode *p) {
return p? p->cnt : 0;
}
TreapNode* rightRotate(TreapNode *y) {
TreapNode *x = y->left, *T2 = x->right;
x->right = y;
y->left = T2;
int temp = y->cnt;
y->cnt = y->cnt - x->cnt + get_cnt(T2);
x->cnt = temp;
return x;
}
TreapNode* leftRotate(TreapNode *x) {
TreapNode *y = x->right, *T2 = y->left;
y->left = x;
x->right = T2;
int temp = x->cnt;
x->cnt = x->cnt - y->cnt + get_cnt(T2);
y->cnt = temp;
return y;
}
TreapNode* insert(TreapNode* root, LL key) {
if (!root) return new TreapNode(key);
root->cnt += 1;
if (key < root->key) {
root->left = insert(root->left, key);
if (root->left->prio > root->prio) root = rightRotate(root);
} else if (key > root->key) {
root->right = insert(root->right, key);
if (root->right->prio > root->prio) root = leftRotate(root);
}
return root;
}
int count_less(TreapNode* root, LL key) {
if (!root) return 0;
if (key == root->key) return get_cnt(root->left);
if (key < root->key) return count_less(root->left, key);
return root->cnt - get_cnt(root->right) + count_less(root->right, key);
}
int main() {
srand(time(NULL));
ios::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
LL l, r; cin >> l >> r;
LL sum = 0, res = 0;
TreapNode* root = NULL;
root = insert(root, sum);
while (n--) {
int x; cin >> x;
sum += x;
res += count_less(root, sum - l + 1) - count_less(root, sum - r);
root = insert(root, sum);
}
cout << res;
return 0;
}