题目:从一串数字中选出三个有序的三元组,问有多少种取法。
分析:数据结构,树状数组。利用树状数组求每个数字两侧大和小的数字的个数为线性。
①按照插入顺序求解data[i]左边比它小的数字记为Lsmall[i];
②则data[i]左边比他大的数字为i-1-Lsmall[i],记为Lbiger[i];
③所有元素插入后,求出data[i]的右侧比他小的元素为:比它小的数字总数-Lsmall[i],记为Rsmall[i];
④则data[i]右侧比他大的数字为N-1-Rsmall[i],记为Rbiger[i];
计算所有的Lsmall[i]*Rbiger[i] + Lbiger[i]*Rsmall[i]。
说明:使用long long类型,防止溢出。
#include <cstring>
#include <cstdio>
int data[20002];
int Lsmall[20002];
int Lbiger[20002];
int C[100001];
int lb(int x) {return x&-x;}
int sum(int k)
{
int value = C[k];
while (k > lb(k))
value += C[(k-=lb(k))];
return value;
}
void add(int k)
{
C[k] += 1;
while (k+lb(k) <= 100000)
C[(k+=lb(k))] += 1;
}
int main()
{
int T, N, Rsmall;
while (~scanf("%d",&T))
while (T --) {
scanf("%d",&N);
for (int i = 1; i <= N; ++ i)
scanf("%d",&data[i]);
memset(C, 0, sizeof(C));
for (int i = 1; i <= N; ++ i) {
Lsmall[i] = sum(data[i]-1);//记录左边小于data[i]的数字个数
Lbiger[i] = i-1-Lsmall[i];//记录左边大于data[i]的数字个数
add(data[i]);
}
long long ans = 0LL;
for (int i = 1; i <= N; ++ i) {
Rsmall = sum(data[i]-1)-Lsmall[i];
ans += 0LL+Lbiger[i]*Rsmall+Lsmall[i]*(N-i-Rsmall);
}
printf("%lld\n",ans);
}
return 0;
}