LA4329乒乓比赛_树状数组

来源:互联网 发布:淘宝店铺被管控怎么办 编辑:程序博客网 时间:2024/04/29 20:05
题目大意:
一条大街上住着n个乒乓球爱好者,经常组织比赛切磋技术。每个人都有一个能力值a[i]。每场比赛需要三个人:两名选手,一名裁判。他们有个奇怪的约定,裁判必须住在两名选手之间,而裁判的能力值也必须在两名选手之间。问一共能组织多少种比赛。

分析:
考虑第i个人,假设a1到ai-1中有ci个比ai小,那么就有(i-1)-ci个比ai大;同理,如果ai+1到an中有di个比ai小,那么就有(n-i)-di个比ai大。更具乘法原理和加法原理,i当裁判就有
ci * (n-i-di) + (i-1-ci)*di
种比赛。(感觉这种思路简直碉堡了)

然后问题就转化为了计算数组c和数组d。这样的话就很容易想到使用树状数组去计算前缀和。
比如输入一个数组,
PS:树状数组的运用主要目的是快速的累加和和修改值,所以这里使用了一个长度为100010的数组C,初始化为0,
如果我们输入  14 3 7 15 1 这样的话,第一次输入了14,所以C[14]+=1,变成1,然后从下面往上开始自增,在算cc[0]时候,传递过去num[i],然后使用加法模板来相加,这样结果就是14左边的小于14的值,dd也是一样的思路,注意dd的计算和cc的计算是相反的,从后往前加(可以想想为什么,注意树状数组相加时候只能从后往前累加!)。
代码如下
#include <iostream>#include <algorithm>using namespace std;#define MAXSIZE 100010int num[MAXSIZE];int C[MAXSIZE];//树状数组的段和int cc[MAXSIZE];//cc[i]表示前面有多少元素比它小int dd[MAXSIZE];//dd[i]表示后面有多少元素比它大int n;//n代表输入元素的个数#define LOWBITE(x) (x&(-x))void add(int num,int increse){while(num<MAXSIZE)  {  C[num]+=increse;  num+=LOWBITE(num);  }}int sum(int num){  int ret=0;  while(num>0)  {    ret+=C[num];num-=LOWBITE(num);  }  return ret;}int main(){  int T;  int i;  int ans;  cin>>T;  while(T--)   { cin>>n; ans=0; for( i=0;i<n;i++) {   cin>>num[i]; }memset(C,0,sizeof(C));memset(cc,0,sizeof(cc));    for(i=0;i<n;i++){  add(num[i],1);  cc[i]=sum(num[i]-1);}memset(C,0,sizeof(C));for(i=n-1;i>=0;i--){  add(num[i],1);  dd[i]=sum(num[i]-1);}for(i=1;i<n;i++){  ans+=cc[i]*(n-i-1-dd[i])+(i-cc[i])*dd[i];}cout<<ans<<endl;   }        return 0;}


0 0