权值线段树
线段树维护的区间是一个值域范围,而不是一段下标的区间。
P1637 三元上升子序列
我们可以考虑中间的数字 \(x\),统计前面比 \(x\) 小的数字个数 \(a_1\), 统计后面比 \(x\) 大的数字个数 \(a_2\),最后的结果为 \(a_1\times a _ 2\)。
使用权值线段树,边处理边询问,从前往后枚举 \(a[i]\),先统计 \([1,a[i] - 1]\) 的位置上的数字总和,然后再在位置 \(a[i]\) 上 \(+1\),这样统计出来的都是之前比 \(a[i]\) 小的数字。
统计比 \(a[i]\) 大的数字同理,只要反过来统计,即从 \(a[n]\) 枚举到 \(a[1]\), 然后询问 \([a[i] + 1, n]\) 的数字总和即可。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
using i64 = long long;
const int N = 100010;
struct SegmentTree {
int l, r;
int val;
}tr[N << 2];
int a[N], n;
void pushup(int u) {
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].val = 0;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v) {
if (tr[u].l == x && x == tr[u].r) {
tr[u].val += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].val;
}
int mid = tr[u].l + tr[u].r >> 1, sum = 0;
if (l <= mid) sum += query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int small[N], big[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, N - 1);
for (int i = 1; i <= n; i++) {
small[i] = query(1, 1, a[i] - 1);
modify(1, a[i], 1);
}
build (1, 1, N - 1);
for (int i = n; i >= 1; i--) {
big[i] = query(1, a[i] + 1, N - 1);
modify(1, a[i], 1);
// cout << big[i] << '\n';
}
i64 ans = 0;
for (int i = 1; i <= n; i++) ans += 1ll * small[i] * big[i];
cout << ans << '\n';
return 0;
}
P1908 逆序对
与上一题类似,但是需要离散化。
标签:数字,int,线段,long,include,统计 From: https://www.cnblogs.com/PlayWithCPP/p/17134345.html