【逆序对 权值树状数组】 楼兰图腾
传送门
题意
即给定 \(n\) 组坐标 \(\left(1, y_{1}\right),\left(2, y_{2}\right), \ldots,\left(n, y_{n}\right)\),其中 \(y_{1}\sim y_{n}\) 是 \(1\sim n\) 的一组排列,
如果三个点 \(\left(i, y_{i}\right),\left(j, y_{j}\right),\left(k, y_{k}\right)\) 满足 \(1 \leq i<j<k \leq n\) 且 \(y_{i}>y_{j}<y_{k}\),则称三点构成 \(V\) 型,
如果三个点 \(\left(i, y_{i}\right),\left(j, y_{j}\right),\left(k, y_{k}\right)\) 满足 \(1 \leq i<j<k \leq n\) 且 \(y_{i}<y_{j}>y_{k}\), 则称这三个点构成 \(\wedge\) 图腾
求出给定的坐标中一共有多少个不同三元组的能构成 \(V\) 型和 \(\wedge\) 型
数据范围
\(n\leq 200000\)
题解
- 维护两个数组,\(great,les\),\(great[i]\) 表示前 \(i\) 个数中有多少比第 \(i\) 个大的,\(les[i]\) 表示比第 \(i\) 小的
- 构成 \(V\) 型和 \(\wedge\) 的是从\(i\) 个前选比其大或小的,其后选一个大或小的,即 \(\mathrm{C}_{n_{pre}}^{1} \times \mathrm{C}_{n_{aft}}^{1}\)
- 第一次正序扫描,前缀和表示个数,维护每个 \(i\) 前比其大的和比其小的
- 第二次倒序扫描,利用第一次的直接计算答案即可
Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
int c[N], a[N];
int pre_great[N], pre_small[N];
int n;
int lowbit(int x){
return x & -x;
}
void add(int x, int y){
for(int i = x; i <= n; i += lowbit(i)) {
c[i] += y;
}
}
int ask(int x){
int res = 0;
for(int i = x; i; i -= lowbit(i)) {
res += c[i];
}
return res;
}
int main(){
cin>>n;
for(int i = 1; i <= n; i ++) cin>>a[i];
for(int i = 1; i <= n; i++){
int now = a[i];
pre_great[i] = ask(n) - ask(now);
pre_small[i] = ask(now - 1);
add(now, 1);
}
memset(c, 0, sizeof c);
ll ans_v = 0, ans_r = 0;
for(int i = n; i >= 1; i --){
int now = a[i];
ans_v += 1ll * pre_great[i] * (ask(n) - ask(now));
ans_r += 1ll * pre_small[i] * ask(now - 1);
add(now, 1);
}
cout << ans_v << ' ' << ans_r;
}

浙公网安备 33010602011771号