HDU 2492 Ping pong (树状数组)
题目大意: 一条大街上住着 n 个乒乓球爱好者,经常比赛切磋技术,每个人有一个不同的技能值ai。每场比赛需要三个人,两名选手,一名裁判。但是裁判必须住在两名选手之间,而且裁判的技能值不能小于或小于两名选手的技能值。问共有多少种比赛。
思路 :枚举裁判,找出位于裁判前面的比裁判技能小的人数和位于裁判后面技能比裁判大的人数,这两个数相乘,并累加到sum中;再找出位于裁判前面技能比裁判大的人数,和位于裁判后面的技能比裁判小的人数,这两个数相乘,将积累加到sum中。
找人数时要用到树状数组: 譬如找位于裁判前面技能比裁判小的人数时,用 pre[i] 存储位于裁判前面的技能为 i 的人数,故位于裁判 (技能为a[k]) 前面的技能比裁判小的人数为pre[1] + per[2] + ... + pre[a[k]] 的累加和。其它的情况可以此类推。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define INF 0x7fffffff
int n,t,a[100002],pre[100002],late[100002];
long long sum,c1[100002],c2[100002],MAX;
int lowbit(int x){
return x & (-x);
}
int SUM(int x,long long c[]){
int res = 0;
while(x > 0){
res += c[x] ;
x -= lowbit(x) ;
}
return res;
}
void add(long long c[],int x,int d){
int t = x;
int p ;
while(t<=MAX){
c[t] += d;
t = t + lowbit(t);
}
}
int main(){
int i,j;
scanf("%d",&t);
while(t--){
sum = 0;
MAX = 0 ;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
MAX = MAX > a[i] ? MAX : a[i] ;
}
memset(pre,0,sizeof(pre));
pre[a[1]] = 1;
memset(late,0,sizeof(late));
for(i=2;i<=n;i++)
late[a[i]] = 1;
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(i=1;i<=100000;i++){
for(j=i - lowbit(i) + 1; j<=i; j++){
c1[i] += pre[j] ;
c2[i] += late[j] ;
}
}
for(i=2;i<n;i++){
add(c2,a[i],-1);
sum += SUM(a[i]-1,c1) * (SUM(MAX,c2) - SUM(a[i],c2)) ;
sum += (SUM(MAX,c1) - SUM(a[i],c1)) * SUM(a[i]-1,c2) ;
add(c1,a[i],1);
}
printf("%I64d\n",sum);
}
return 0;
}