问题 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 ;
}