UVA1428 Ping pong(树状数组)
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4174
题意:
一条大街上住着n个乒乓球爱好者,他们经常组织乒乓球比赛且每个人的能力值ai都不同.每次比赛需要2个比赛者和一个裁判,他们有一个奇怪的规定:当裁判的那个人必须住在这两个比赛者之间,且裁判的能力值也必须在这两个人之间.问一共有多少种比赛组织方式.
输入:首先是T(1<=T<=20),表示实例个数.对于每个实例第一行是一个n(3<=n<=20000),然后是n个不同的整数即a1,a2…an(1<=ai<=100000),按照他们的住所位置从左到右给出每个人的能力值.
输出:比赛组织的总数.
分析:
对于第i个(任意一个)裁判,他能组织多少种比赛呢?
假设a1到ai-1有S_L[i]个能力值比ai小的,那么有i-1-S_L[i]个能力值比ai大的.同理可得S_R[i]为从i+1到n有B[i]个人能力值比ai小.那么i当裁判能组织的比赛种数是:S_L[i]*(n-i-S_R[i])+(i-1-S_L[i])*S_R[i]种. 我们只需要求出所有裁判的S_L[i]和S_R[i]即可求出所有比赛种数.
如何求S_L[i]呢(即在a[i]左边且比a[i]值小的数个数)? 我们令X[n]作为标记数组,假设当前我们扫描到了第i个点,此时如果X[y]==1,那么表示第i个点左边出现了一个a[k]==y的值且k<i。那么这时,我们只要用树状数组计算X[n]数组中区间[1, a[i]-1]的和即可(想想是不是)。
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=20000+100; const int MAX_NUM=100000+100; int S_L[MAXN],S_R[MAXN]; int x[MAX_NUM]; int c[MAX_NUM]; int a[MAXN];//能力值 int max_a; int lowbit(int i) { return i&(-i); } int sum(int i)//求从A[1]到A[i]的和 { int res=0; while(i>0) { res += c[i]; i-=lowbit(i); } return res; } void add(int i,int d)//使得C数组的所有包含A[i]的项都加上d { while(i<=max_a)//这里max_a 是能力值的上限 { c[i]+=d; i += lowbit(i); } } int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); max_a=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); max_a=max(max_a,a[i]); } memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { S_L[i]=sum(a[i]); add(a[i],1); } memset(c,0,sizeof(c)); for(int i=n;i>=1;i--) { S_R[i]=sum(a[i]); add(a[i],1); } long long ans=0; for(int i=1;i<=n;i++) { ans += (long long)S_L[i]*(n-i-S_R[i])+(long long)(i-1-S_L[i])*S_R[i]; } printf("%lld\n",ans);//注意UVA关于64位整数的格式 } return 0; }