POJ3928 - Ping pong
题目大意
一条大街上从西到东依次住着N个乒乓球爱好者,他们经常举办比赛。每个人都有一个不同的技能值a[i]。每场比赛需要三个人,一个裁判加两个爱好者,不过比赛有一个要求就是裁判的技能值需要在两个爱好者的中间,并且居住的位置也要求在两个爱好者的中间,问总共能够举办多少场比赛。
题解
LRJ《训练指南》上树状数组的例题,我们枚举每一个人当裁判的情况,对于第i个人当裁判,假设a[1]到a[i-1]有pre[i]个比a[i]小,那就有i-1-pre[i]个人比大。同样假设a[i+1]到a[n]有suc[i]个比a[i]小,则有n-i+1-suc[i]个人比a[i]大,那么第i个人当裁判将有pre[i]*(n-i+1+suc[i])+suc[i]*(i-1-pre[i])场比赛,把每个人当裁判能够举办的比赛场数加起来就是总共能够举办的比赛场数。
代码
#include<iostream> #include<cstring> #include<cstdio> #define MAXN 20005 #define MAXS 100005 using namespace std; int n; int lowbit(int x) { return (x&-x); } int a[MAXN],c[MAXS],pre[MAXN],suf[MAXN]; int sum(int x) { int ret=0; while(x>0) { ret+=c[x]; x-=lowbit(x); } return ret; } void add(int x,int d,int p) { while(x<=p) { c[x]+=d; x+=lowbit(x); } } int main(void) { int i,T,maxx; long long ans; cin>>T; while(T--) { memset(c,0,sizeof(c)); memset(pre,0,sizeof(pre)); cin>>n; maxx=-1; for(i=1; i<=n; i++) { scanf("%d",&a[i]); if(maxx<a[i]) maxx=a[i]; } for(i=1; i<n; i++) { add(a[i],1,maxx); pre[i]=sum(a[i]-1); } memset(suf,0,sizeof(suf)); memset(c,0,sizeof(c)); for(i=n; i>1; i--) { add(a[i],1,maxx); suf[i]=n-i+1-sum(a[i]); } ans=0; for(i=2; i<n; i++) ans+=pre[i]*suf[i]+(i-1-pre[i])*(n-i-suf[i]); cout<<ans<<endl; } return 0; }