LA4329乒乓比赛_树状数组

题目大意:
一条大街上住着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 100010
int 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值