题意:对于给定的一个长度为n序列a,对于每个位置i,若左边存在一个数a[l],右边存在一个数a[r],满足a[l] < a[i] < a[r]或a[l] > a[i] > a[r],则(l, r)构成一对,求总共有多少对。
思路:虽然先学的是线段树,但是这类问题也可以用树状数组解决。考虑2个数组c和d,对于位置i,c[i]表示位置i左边有c[i]个数比它小,d[i]表示位置i右边有d[i]个数比它小。则最后的结果就是
∑i=1nc[i]∗(n−i−d[i])+(i−1−c[i])∗d[i]
代码:
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
const int N = 2e4 + 10;
const int M = 1e5 + 10;
int s[M << 2];
int c[M], d[M];
int a[N];
int n;
int lowbit(int x) {
return x & (-x);
}
void add(int x) {
while (x <= M) {
s[x]++;
x += lowbit(x);
}
}
int sum(int x) {
int res = 0;
while (x > 0) {
res += s[x];
x -= lowbit(x);
}
return res;
}
int main() {
int t_case;
scanf("%d", &t_case);
for (int i_case = 1; i_case <= t_case; i_case++) {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(s, 0, sizeof(s));
for (int i = 1; i <= n; i++) {
c[i] = sum(a[i] - 1);
add(a[i]);
}
memset(s, 0, sizeof(s));
for (int i = n; i >= 1; i--) {
d[i] = sum(a[i] - 1);
add(a[i]);
}
long long res = 0;
for (int i = 2; i < n; i++)
res += 1LL * c[i] * (n - i - d[i]) + 1LL * (i - 1 - c[i]) * d[i];
printf("%lld\n", res);
}
return 0;
}