http://poj.org/problem?id=3928
题意:有n个乒乓球爱好者,每个人都有不同的权能值a[i],每场比赛需要三个人,两名选手,一名裁判。有规定,裁判必须住在两名选手中间,并且技能值也要在两名选手中间。问一共能组织多少场比赛。
思路:只考虑第i(2 <= i <= n-1)个人当裁判的情况。维护两个数组c[]和d[]。c[i]表示a[1]~a[i-1]有c[i]个数比a[i]小,那么就有(i-1-c[i])个比a[i]大。同样的,d[i]表示a[i+1]~a[n]有d[i]个数比a[i]小,那么就有(n-i-d[i])个比a[i]大。根据乘法原理和加法原理,i当裁判时,有 c[i]*(n-i-d[i]) + d[i]*(i-c[i]-1)种比赛。这样问题就转化为求c[i]和d[i]。
求c[i]:从左到右扫描所有的a[i],令x[j]表示到目前所有的a[i]中是否存在一个a[i] == j( x[j] = 0 表示不存在,x[j] = 1表示存在).
则c[i]就是前缀和x[1]+x[2]+....+x[ a[i] -1 ]。用树状数组求前缀和即区间统计,当扫描到a[i]时,先更新a[i](往右走,边走边“往上爬”),然后求前缀和(往左走,边走边“往上爬”)。
初始时所有的x[i] = 0。
#include <stdio.h> #include <string.h> #include <algorithm> #define LL long long using namespace std; const int maxn = 100010; const int maxm = 20010; int test,n,a[maxm],c[maxm],d[maxm],x[maxn]; int lowbit(int x) { return x & (-x); } int sum(int k) { int ans = 0; while(k) { ans += x[k]; k -= lowbit(k); } return ans; } void update(int k) { while(k <= 100000) { x[k] += 1; k += lowbit(k); } } int main() { while(~scanf("%d",&test)) { while(test--) { scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&a[i]); memset(c,0,sizeof(c)); memset(d,0,sizeof(d)); memset(x,0,sizeof(x)); for(int i = 1; i <= n; i++) { c[i] = sum(a[i]-1); //求c[i] update(a[i]); } memset(x,0,sizeof(x)); for(int i = n; i >= 1; i--) { d[i] = sum(a[i]-1);//求d[i] update(a[i]); } LL ans = 0; for(int i = 2; i < n; i++) ans += (LL)c[i]*(n-i-d[i]) + (LL)d[i]*(i-c[i]-1); printf("%lld\n",ans); } } return 0; }
Poj 3928 Ping pong(树状数组),布布扣,bubuko.com
原文:http://blog.csdn.net/u013081425/article/details/21550583