题目的大致意思是:有n个乒乓球手,然后他们都有一个能力值,然后需要每三个选手可以组成一场比赛,这场比赛要求,中间的那个人作为裁判,而且中间的人的能力值必须在两位选手之间,这个比赛才是合法的比赛,问这个这些选手一共能组织成多少场比赛?
解题思路:首先对单个人作为裁判进行研究,假设标号i的人,假设前面小于他能力值的人为C[i],那么前面大于他能力值的即为i - 1 - C[i],假设后面小于他能力值的人为D[i],那么后面大于他能力值的即为n - i - D[i],根据乘法原理和加法原理,题目的答案即为:
所以最终题目即转化为求C[i], D[i].
在求C[i],D[i]之前,先引入树状数组的知识,有如下的图:
建立一个lowbit数组我们构建lowbit[i] = j,j是i中二进制位上从右往左数,第一个为1的位置所代表的数(例如lowbit[20] = 4, 因为(20) = 10100, lowbit[20] = 100 = 4)。
从而我们可以定义数组C的意义:
即为前缀和。
那么对于两个操作,一个是修改数组中某一个值,一个是查询区间(L,R)的和,是树状数组的两个基本操作,对于求和,刚才已经介绍了前缀和,我们可以发现:
而对于修改操作,我们同样可以得到:
d为该位置上A[X]的改变值。
对于题目中C[i],D[i]的求解,我们还需要转化一下模型才能回到树状数组上:
我们对于其能力值进行研究,对于数组A[i]代表能力值为i的选手是否存在,如果A[i]=1代表存在,A[i] = 0代表不存在。对于每一个选手它的能力值我们就动态的加入其中,要求它的C[i]即为求加入后的前缀和,所以最早的初始值A都为0,没加入一个选手就领A[num[i]] = 1,所以其为树状数组的标准操作。求D[i]也是同理的,不过就是要反向操作。
另外对于lowbit的求解有,lowbit[i] = (i & (-i)),用之前的定义叙述的找,竟然会让它超时,难以想象。
#include "stdio.h"
#include "string.h"
#include "math.h"
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAXM 1
#define MAXN 1
#define max(a,b) a > b ? a : b
#define min(a,b) a < b ? a : b
#define Mem(a,b) memset(a,b,sizeof(a))
int Mod = 1000000007;
double pi = acos(-1.0);
double eps = 1e-6;
typedef struct{
int f,t,w,next;
}Edge;
Edge edge[MAXM];
int head[MAXN];
int kNum;
typedef long long LL;
void addEdge(int f, int t, int w)
{
edge[kNum].f = f;
edge[kNum].t = t;
edge[kNum].w = w;
edge[kNum].next = head[f];
head[f] = kNum ++;
}
int n, T;
int lowbit[100005];
int pC[100005];
int pD[100005];
int num[100005];
int X[100005];
void init(){
lowbit[0] = 0;
for(int i = 1; i <= 100000; i ++){
lowbit[i] = (i & (-i));
}
}
void Add(int x){
while(x <= 100000){
X[x] += 1;
x += lowbit[x];
}
}
int Sum(int x){
int sum = 0;
while(x){
sum += X[x];
x -= lowbit[x];
}
return sum;
}
void solve(){
Mem(X, 0);
for(int i = 1; i <= n; i ++){
scanf("%d", &num[i]);
}
for(int i = 1; i <= n; i ++){
pC[i] = Sum(num[i]);
Add(num[i]);
}
Mem(X, 0);
for(int i = n; i > 0; i --){
pD[i] = Sum(num[i]);
Add(num[i]);
}
LL sum = 0;
for(int i = 1; i <= n; i ++){
sum += (1LL * pC[i] * (n - i - pD[i]) ) + (1LL * (i - pC[i] - 1) * pD[i] );
}
cout<<sum<<endl;
}
int main()
{
// freopen("d:\\test.txt", "r", stdin);
init();
while(cin>>T){
while(T--){
cin>>n;
solve();
}
}
return 0;
}