题目大意:给一串长度为m的数字,找出满足0 < i < x < j < m,且num[i] <= num[x] <= num[j] 或者 num[i] >= num[x] >=num[j] 的总对数
网络上的树桩数组版本基本都是错的,可能数据弱的原因吧.都能AC
其实解法很简单:
分别统计每个数字左右两边的正序数和逆序数,然后相乘. 求和即可
用树状数组统计正序数和逆序数
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXXX 100010
#define MAXX 20010
typedef long long ll;
using namespace std;
int c[MAXXX];
int num[MAXX];
int left_min[MAXX];
int left_max[MAXX];
int right_min[MAXX];
int right_max[MAXX];
inline void Update(int i,int diff){
while(i<MAXXX){
c[i]+=diff;
i+=i&(-i);
}
}
inline int GetSum(int i){
int s=0;
while(i>0){
s+=c[i];
i-=i&(-i);
}
return s;
}
int main(){
int n;
scanf("%d",&n);
while(n--){
int m;
scanf("%d",&m);
int k=0;
for(int i=0;i<m;i++){
scanf("%d",&num[i]);
num[i]++;
left_min[i]=GetSum(num[i]);
left_max[i]=k-left_min[i];
Update(num[i],1);
k++;
}
memset(c,0,sizeof(c));
int kk=0;
for(int i=m-1;i>=0;i--){
right_min[i]=kk-GetSum(num[i]-1);
right_max[i]=kk-right_min[i];
Update(num[i],1);
kk++;
}
ll ans=0;
for(int i=0;i<m;i++){
ans+=left_min[i]*right_min[i]+left_max[i]*right_max[i];
}
printf("%I64d\n",ans);
memset(c,0,sizeof(c));
}
return 0;
}