题意分析
这道题和逆序对的思路很相似。
首先可以想到的是,如果枚举两个人,然后算中间有几个裁判,不行。枚举两个人也要
O(n2)
O
(
n
2
)
,自然想到要枚举裁判。
如果要枚举裁判的话,其实就是求左边比裁判小,右边比裁判大,或者左边比裁判大,右边比裁判小的人数。 然后算个乘法求和即可。
自然就会想到求逆序对的个数那个题,统计方法类似的。然后推一个怎么统计答案的式子就好了。
代码总览
#include <bits/stdc++.h>
#define rep(i,a,b) for (int i = a; i<=b; ++i)
using namespace std;
const int nmax = 1e5 + 1000;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef double db;
typedef struct {
int l, r, val;
int mid() {return (l + r) >> 1;}
} Tree;
Tree tree[nmax << 2];
ll L[nmax], R[nmax], a[nmax];
int t, n;
void pushup(int rt) {
tree[rt].val = tree[rt << 1].val + tree[rt << 1 | 1].val;
}
void build(int l , int r, int rt) {
tree[rt].l = l , tree[rt].r = r;
tree[rt].val = 0;
if (l == r) return;
build(l, tree[rt].mid(), rt << 1);
build(tree[rt].mid() + 1 , r, rt << 1 | 1);
}
void update(int pos , int rt) {
if (tree[rt].l == tree[rt].r) {
tree[rt].val = 1;
return;
}
if (pos <= tree[rt].mid()) update(pos, rt << 1);
else update(pos, rt << 1 | 1);
pushup(rt);
}
ll query(int l , int r , int rt) {
if (tree[rt].l >= l && tree[rt].r <= r) return tree[rt].val;
if (r <= tree[rt].mid()) return (ll)query(l, r, rt << 1);
else if (l > tree[rt].mid()) return (ll)query(l, r, rt << 1 | 1);
else return (ll)query(l, tree[rt].mid(), rt << 1 ) + (ll)query(tree[rt].mid() + 1, r, rt << 1 | 1);
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
memset(L, 0, sizeof L);
memset(R, 0, sizeof R);
ll mx = 0;
rep(i, 1, n) {scanf("%lld", &a[i]); mx = max(mx, a[i]);}
build(1, mx, 1);
rep(i, 1, n) {
L[i] = query(1, a[i], 1);
update(a[i], 1);
}
build(1, mx, 1);
for (int i = n; i >= 1; --i) {
R[i] = query(1, a[i], 1);
update(a[i], 1);
}
ll ans = 0;
rep(i, 1, n) {
if (i == 1 || i == n) continue;
else {
ll temp = (ll) L[i] * ((ll)n - i - R[i]);
ll temp2 = (ll) R[i] * ((ll)i - 1 - L[i]);
ans += temp + temp2;
}
}
printf("%lld\n", ans);
}
return 0;
}