// 权值线段树解法 #include 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; }