题目链接: poj 3928 Ping pong
题目大意: 有
n
n
n个人,每人位置固定
(
1
−
n
)
(1-n)
(1−n),每个人的乒乓球技能值为
a
i
a_i
ai,让你从中选出
3
3
3人,其中两人比赛,另一人为裁判,要求裁判的位置和技能值都必须在另两人之间,问有多少种可能的组合方式(保证技能值互不相同)。
题目分析: 这道题相当于是要求在一个数列中找三个数,使得中间的数位三数中的中位数,因此我们可以枚举每个数,然后在这个数为中位数时求所有的答案,最后加入到总答案中。这样,就可以正序倒序分别遍历一遍,然后统计每个数的前后各有多少比他小的数,最后再次遍历,统计答案。在处理前后比他小的数时其实就是一个二维偏序问题,可以用树状数组优化。
题目代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define mxn 200005
#define LL long long
using namespace std;
LL arr[mxn],maxn,minn;
LL tree[mxn],c[mxn],d[mxn];
LL lowbit(LL x){
return x&(-x);
}
void Update(LL pos,LL v){
for(LL i=pos;i<=mxn;i+=lowbit(i))
tree[i]+=v;
}
LL Query(LL pos){
LL ret=0;
for(LL i=pos;i;i-=lowbit(i))
ret+=tree[i];
return ret;
}
int main()
{
LL t,n;
scanf("%lld",&t);
while(t--){
LL ans=0;
maxn=0;
memset(tree,0,sizeof(tree));
scanf("%lld",&n);
for(LL i=1;i<=n;i++){
scanf("%lld",&arr[i]);
maxn=max(maxn,arr[i]);
}
for(LL i=1;i<=n;i++){
c[i]=Query(arr[i]);
Update(arr[i],1);
}
memset(tree,0,sizeof(tree));
for(LL i=n;i>=1;i--){
d[i]=Query(arr[i]);
Update(arr[i],1);
}
for(LL i=1;i<=n;i++)
ans+=c[i]*(n-i-d[i])+d[i]*(i-1-c[i]);
printf("%lld\n",ans);
}
return 0;
}