问题 F: 对战 II

题目描述
在⼀条街道上有n个⼈,他们都喜欢打乒乓球。任意两个⼈的家的位置都不相同,按顺序标为1,2,…,n。每个⼈都有⼀定的⽔平,用两两不等的整数表示。
当两个⼈想打球的时候,会找另⼀个⼈作为裁判,并到裁判家里进⾏⼀场较量。出于某种原因,他们希望裁判的⽔平介于两⼈之间;同时,他们希望两个⼈到裁判家的总路程不超过两个⼈的家的距离。
对于两场较量,如果打球的两个⼈不完全相同或者裁判不同,我们就认为这两场较量不同。求不同的较量的总数。

输入
输⼊包含多组数据
输⼊的第⼀⾏是⼀个整数T,表示数据组数;
每组数据占⼀⾏,包含n+1个整数:n,a1,a2,···,an。其中a1,a2,···,an表示家位于相应位置的⼈的⽔平。

输出
对每组数据,用⼀⾏输出⼀个整数,表示不同的较量的总数。

样例输入
复制样例数据
1
3 1 2 3
样例输出
1

提示
对于40%的数据,有n≤1000;
对于所有数据,有T≤20,3≤n≤100000,每个⼈的⽔平都是不超过200000的正整数。

题解:题目要求满足ai < aj < ak || ai > aj > ak ,其中i
< j < k , 满足这两个条件的个数有多少, 我们来枚举中间的j,判断左面小于(大于)它 , 右边大于(小于)它的个数, 然后相乘, 解这种题目, 类似于用树状数组求解逆序对, 不过这个数据范围好像有点小, 不需要进行离散化就可以了,先将左边小于a[i] 的个数存储到l[i] 中 , 直接插入a[i],同理将右边大于a[i]的存储到r[i] , 插入a[i] , 不过在处理大于a[i] 的过程中,我们用反向的,最后枚举(2 , n -1) 作为中间点, 然后进行 判断左面小于(大于)它 , 右边大于(小于)它的个数, 然后相乘 , 即l[i] * (n - i - r[i]) + r[i] * (i - 1 - l[i]) ,
// (n-i-r[i]) 表示右边比 i 大的
// (i-l[i]-1) 表示左边比 i 大的

#include <iostream>
#include <cstring>
using namespace std;
const int N = 2e5 + 10 ;
typedef long long ll ;
ll a[N] , c[N] , l[N] , r[N] ;
ll lowbit(int x)
{
	return x & -x ;
}
ll ask(int x)
{
    ll ans = 0 ;
	for( ;x > 0 ; x -= lowbit(x))
	 ans += c[x];
	return ans ;
}
ll n ;
void add(int x , int y)
{
	for( ; x < N ; x += lowbit(x))
	 c[x] += y ;
}
int main()
{
    int t ;
    cin >> t ;
	while(t --)
	{
		memset(c , 0 , sizeof c) ;
		cin >> n ; 
		memset(l , 0 , sizeof l) ;
		memset(r , 0 , sizeof r) ;
		for(int i = 1 ;i <= n ;i ++) cin >> a[i] ;
		for(int i = 1 ;i <= n ;i ++)
		 {
		 	l[i] = ask(a[i] - 1) ;
		 	add(a[i] , 1) ;
		 }
		 memset(c , 0 , sizeof c) ;
		 for(int i = n ;i >= 1 ;i --)
		  {
		  	r[i] = ask(a[i] - 1) ;
		  	add(a[i] , 1) ;
		  }
		  ll ans = 0 ;
		  for(int i = 2 ;i <= n - 1 ;i ++)
		   ans += l[i] * (n - i - r[i]) + r[i] * (i - 1 - l[i]) ;
	      cout << ans << endl ;
	}
	return 0 ;
} 
posted @ 2019-10-10 14:55  spnooyseed  阅读(102)  评论(0编辑  收藏  举报