You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

82 lines
2.2 KiB

2 years ago
// 权值线段树解法
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int a[N], b[N];
int n;
// 线段树区间和模板
#define ls u << 1
#define rs u << 1 | 1
struct Node {
int l, r; // 范围
int sum; // 区间内数字个数
} tr[N << 2]; // 4倍空间
// 创建
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) return;
int mid = l + r >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
}
// 向上更新统计信息
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum;
}
// 修改点的值
void modify(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].sum += v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid)
modify(ls, x, v);
else
modify(rs, x, v);
pushup(u);
}
// 查询区间和
int query(int u, int l, int r) {
if (l > r) return 0;
if (tr[u].l == l && tr[u].r == r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
if (mid < l) return query(rs, l, r);
if (r <= mid) return query(ls, l, r);
return query(ls, l, mid) + query(rs, mid + 1, r);
}
LL ans;
int main() {
cin >> n;
// 一般使用线段树都喜欢从下标1开始所以我们在处理数据时尽量让下标从1开始
// b[]在下面的代码中有两个阶段的含义目前的含义是a[i]的备份因为一会a[]由小到大排序后原始数组就丢失了位置信息就不见了需要b备份出来
for (int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
// ① 原始数组由小到大排序
sort(a + 1, a + n + 1);
// ② 离散化遍历原始数组b[i],找出它的排名即b[2]=3表示第2个输入的是排名第3的
for (int i = 1; i <= n; i++) b[i] = lower_bound(a + 1, a + n + 1, b[i]) - a;
// 构建线段树
build(1, 1, n); // n个数字
// 边查边添加
for (int i = 1; i <= n; i++) {
modify(1, b[i], 1); // 占据b[i]这个位置
ans += query(1, b[i] + 1, n); // 出现的比我早,号还比我大的有多少个
}
// 输出
printf("%lld\n", ans);
return 0;
}