时间限制:3.000秒
一条大街上住着n个乒乓球爱好者,经常组织比赛切磋技术。每个人都有一个不同的技能值ai。每场比赛需要三个人:两名选手一名裁判。裁判必须住在两名选手中间,且技能值也在两人之间。问一共能组织多少种比赛。
二叉索引树入门例题。
设i为第i个人,令c[i]为从a[1]到a[i-1]中有多少比a[i]小,d[i]为从a[n[到a[i+1]中有多少比a[i]小。那么i当裁判就有c[i] * (n - i - d[i]) + d[i] * (i - c[i] - 1)种比赛。用求前缀和的方法求出c和d,然后遍历求和即可。
#include
#include
#include
using namespace std;
static const int MAXN = 100000 + 10;
class BIT { // 二叉索引树
public:
int C[MAXN];
public:
BIT() { memset(C, 0, sizeof(C)); }
BIT(const BIT &b) { memcpy(this, &b, sizeof(BIT)); }
private:
int lowbit(const int &x) const { return x & -x; }
public:
void init() { memset(C, 0, sizeof(C)); }
int sum(int x) const { // 求前缀和S[x]
int ret = 0;
while(x > 0) { ret += C[x], x -= lowbit(x); }
return ret;
}
void add(int x, const int &d) { // 修改索引树,令A[x] += d
while(x < MAXN) {
C[x] += d; x += lowbit(x);
}
}
};
int a[MAXN], c[MAXN], d[MAXN], n;
BIT tree;
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
tree.init();
for(int i = 1; i <= n; ++i) {
tree.add(a[i], 1);
c[i] = tree.sum(a[i] - 1);
}
tree.init();
for(int i = n; i >= 1; --i) {
tree.add(a[i], 1);
d[i] = tree.sum(a[i] - 1);
}
long long ans = 0;
for(int i = 1; i <= n; ++i) {
ans += (long long)c[i] * (n - i - d[i]) + (long long)d[i] * (i - c[i] - 1);
}
printf("%lld\n", ans);
}
}