UVA1428 Ping pong(树状数组)

题意:一条大街上住着n个乒乓球爱好者,他们经常组织乒乓球比赛且每个人的能力值ai都不同.每次比赛需要2个比赛者和一个裁判,他们有一个奇怪的规定:当裁判的那个人必须住在这两个比赛者之间,且裁判的能力值也必须在这两个人之间.问一共有多少种比赛组织方式.其实就是一个数列(不一定有序)要你找任意三个数递增或递减的组合数

思路:一个有点像经典滑窗的题目。

           对于某个位置i,我们设L[i]为i左边比它小的数的个数,那么在左边比它大的数的个数就是i-L[i]-1

           对应的,我们设R[I]为i右边比它小的数的个数,那么在右边比它大的数的个数就是n-i-R[i]

           那么显然的对于每个位置的组合数就是 L[i]*(n-i-R[i]) + R[i]*(i-L[i]-1)了,自己推一下看看是不是

           好了,那么现在问题就来到了如何来求这个L[i]和R[i],一开始我算是暴力滑窗来做,显然n^2复杂度超时超得不行了。主要是得解决一个问题就是如何找到在i左边的并且比它小的数呢?这里有一个十分巧妙的转换思路,那么就是用树状数组,其中下标为A[i](能力值),为什么这样可以呢?可以知道树状数组在求和时的sum函数是返回A[1]...A[R]的和,我们用能力值作为下标,如果这个数存在就+1,标记有这个数。那么标记之前求和一次,返回的就是比它小的个数的数量,因为一开始输入的能力值虽然不是有序的,可是在树状数组求和的时候就变成从小到大的了,因为能力值大的在后面,举个简单的例子,输入能力值为 4 5 3 6那么第一个数的时候执行一次求和,显然这个时候5和3 6都还没有标记,所以为0,然后标记C[4]++,(用能力值为下标),然后到5,先执行一次求和,显然答案就为1,之后C[5]++,到了3,先求一次和,因为树状数组求和返回的是C[1]+C[2]+C[3],那么显然答案为0,然后C[3]++,到了6,执行求和,答案显然为3,然后标记。自己想一下这个过程,是不是就很巧妙的将要找在i左边并且比它小的数的个数这个问题完美解决了。


#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 20000+10
#define maxnn 100000+10
#define LL long long
int cas=1,T;
int a[maxn];
int L[maxn];
int R[maxn];
int c[maxnn];
int maxx;
int lowbit(int i)
{
	return i&(-i);
}
int sum(int i)
{
	int ans = 0;
	while (i)
	{
		ans +=c[i];
		i-=lowbit(i);
	}
	return ans;
}
void add(int i,int d)
{
	while (i<=maxx)
	{
		c[i]+=d;
		i+=lowbit(i);
	}
}

int main()
{
	//freopen("in","r",stdin);
	scanf("%d",&T);
	while (T--)
	{
		int n;
		scanf("%d",&n);
		memset(a,0,sizeof(a));
		maxx=0;
		for (int i = 1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			maxx = max(maxx,a[i]);
		}
        memset(L,0,sizeof(L));
		memset(R,0,sizeof(R));
		memset(c,0,sizeof(c));
		for (int i = 1;i<=n;i++)
		{
			L[i] = sum(a[i]);
			add(a[i],1);
		}
		memset(c,0,sizeof(c));
		for (int i = n ;i>=1;i--)
		{
			R[i] = sum(a[i]);
			add(a[i],1);
		}
		LL ans = 0;
		for (int i = 1;i<=n;i++)
		{
            ans += ( (LL)(L[i]*(n-i-R[i])) + (LL)(R[i]*(i-L[i]-1)) );
		}
		printf("%lld\n",ans);
	}
	//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
	return 0;
}


N (3 ≤ N ≤ 20000) ping pong players live along a west-east street(consider the street as a line segment).
Each player has a unique skill rank. To improve their skill rank, they often compete with each other. If
two players want to compete, they must choose a referee among other ping pong players and hold the
game in the referee’s house. For some reason, the contestants can’t choose a referee whose skill rank is
higher or lower than both of theirs. The contestants have to walk to the referee’s house, and because
they are lazy, they want to make their total walking distance no more than the distance between their
houses. Of course all players live in different houses and the position of their houses are all different. If
the referee or any of the two contestants is different, we call two games different. Now is the problem:
how many different games can be held in this ping pong street?
Input
The first line of the input contains an integer T (1 ≤ T ≤ 20), indicating the number of test cases,
followed by T lines each of which describes a test case.
Every test case consists of N + 1 integers. The first integer is N, the number of players. Then N
distinct integers a1, a2 . . . aN follow, indicating the skill rank of each player, in the order of west to east
(1 ≤ ai ≤ 100000, i = 1 . . . N).
Output
For each test case, output a single line contains an integer, the total number of different games.
Sample Input
1
3 1 2 3
Sample Output

1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值