先给出权值线段树的概念:
对一个数组 a a a 构造一个数组 b b b,其 b j b_j bj 表示 a a a 中 i i i 出现的次数,用 b b b 建立的线段树就是权值线段树
不那么通俗的理解:
在普通的线段树中一个节点通常对应一段区间,而在权值线段树中一个节点通常对应一段值域。
例题1(洛谷P1637):
在含有
n
n
n 个整数的序列
a
1
,
a
2
,
…
,
a
n
a_1,a_2,\ldots,a_n
a1,a2,…,an 中,三个数被称作 thair
当且仅当
i
<
j
<
k
i<j<k
i<j<k 且
a
i
<
a
j
<
a
k
a_i<a_j<a_k
ai<aj<ak。
求一个序列中 thair
的个数。
很容易发现我们可以枚举
j
j
j,算出可行的
i
i
i 的个数,再算出可行的
k
k
k 的个数,根据乘法原理
i
×
k
i \times k
i×k 就是以
j
j
j 为中点的 thair
个数(原题数据很水
O
(
n
2
)
O(n^2)
O(n2) 暴力能过QAQ)。
而根据权值线段树,比 j j j 小的就是 1 1 1 到 a i − 1 a_i-1 ai−1 的数的个数,比 j j j 大的就是 a i + 1 a_i+1 ai+1 到 1 0 5 10^5 105 的数的个数,而更新 a i a_i ai 个数只需要 a i a_i ai 到 a i a_i ai 加 1 1 1 即可,但 k k k 还要求下标必须大于 j j j 的下标,所以只需要再倒着跑一遍线段树即可。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2 * 1e5 + 5;
int n, m, a[N], tree[N << 2], les[N], great[N], cnt[N];
void push_up(int cur){
tree[cur] = tree[cur * 2] + tree[cur * 2 + 1];
}
void build(int cur, int l, int r){
if(l == r){
tree[cur] = 0;
return ;
}
int mid = (l + r) / 2;
build(cur * 2, l, mid);
build(cur * 2 + 1, mid + 1, r);
push_up(cur);
}
void update(int cur, int l, int r, int qx, int qy, int val){
if(qx > r || qy < l){
return ;
}if(qx <= l && r <= qy){
tree[cur] ++;
return ;
}
int mid = (l + r) / 2;
update(cur * 2, l, mid, qx, qy, val);
update(cur * 2 + 1, mid + 1, r, qx, qy, val);
push_up(cur);
}
int query(int cur, int l, int r, int qx, int qy){
if(qx > r || qy < l){
return 0;
}if(qx <= l && r <= qy){
return tree[cur];
}
int mid = (l + r) / 2;
return query(cur * 2, l, mid, qx, qy) + query(cur * 2 + 1, mid + 1, r, qx, qy);
}
//上面全是线段树模板QAQ
signed main(){
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> a[i];
}
build(1, 1, 1e5);//第一遍线段树
for(int i = 1; i <= n; i ++){//由于之前加进去的数肯定比 a[i] 下标小,所以正序即可
les[i] = query(1, 1, 1e5, 1, a[i] - 1);//统计比 a[i] 小的数的个数
update(1, 1, 1e5, a[i], a[i], 1);//将 a[i] 计入个数
}
build(1, 1, 1e5);//第二遍线段树
int ans = 0;
for(int i = n; i >= 1; i --){
great[i] = query(1, 1, 1e5, a[i] + 1, 1e5);//统计比 a[i] 大的数的个数
update(1, 1, 1e5, a[i], a[i], 1);//还是将 a[i] 计入个数
ans += les[i] * great[i];//统计答案
}
cout << ans;
return 0;
}
完结撒花!!!