如果之前做过树状数组求逆序数那再做这道题就很轻松了。
枚举以每个人为裁判,有两种情况,左边的人值比裁判小右边的人比裁判大,左边的人值比裁判大右边的人值比裁判大。左右两边相乘,两种情况相加就是答案。第一种情况需要统计位于裁判左边值比它小的个数和位于右边值比它大的个数,而第二种情况可以由第一种情况推出来。
所以重点就在统计位于裁判左边值比它小的个数和位于右边值比它大的个数。
这就需要用树状数组了。
裁判左边值比它小的个数:原数列中从小到大依次枚举,计算树状数组A从1到它在原数列正序位置的和,为左边小于该数字的个数,然后将该位置更新为1.
裁判右边值比它大的个数:原数列中从大到小依次枚举,计算树状数组B从1到它在原数列逆序位置的和,为右边小于该数字的个数,然后将该位置更新为1.
至于第二种情况可以分别由左、右边数字个数减去对应个数求得。
再就是注意最后答案要用longlong。为了测试这里我特地WA了一次。。
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #define MAXN 20005 #define LL long long using namespace std; int arr1[MAXN],arr2[MAXN]; int Ls[MAXN],Rb[MAXN]; struct Num { int val,pos1,pos2; void set(int a,int b,int c) { val=a; pos1=b; pos2=c; } }; bool cmp(Num a,Num b) { return a.val<b.val; } Num x[MAXN]; int n; int lowbit(int v) { return (-v)&v; } int Sum(int pos,int a[]) { int sum=0; while(pos>0) { sum+=a[pos]; pos-=lowbit(pos); } return sum; } void Add(int pos,int val,int a[]) { while(pos<=n) { a[pos]+=val; pos+=lowbit(pos); } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); int val; for(int i=1; i<=n; ++i) { scanf("%d",&val); x[i].set(val,i,n+1-i); } memset(arr1,0,sizeof(arr1)); memset(arr2,0,sizeof(arr2)); memset(Ls,0,sizeof(Ls)); memset(Rb,0,sizeof(Rb)); sort(x+1,x+n+1,cmp); for(int i=1; i<=n; ++i) { int &cur=x[i].pos1; Ls[cur]=Sum(cur,arr1); Add(cur,1,arr1); } for(int i=n; i>=1; --i) { int &cur=x[i].pos2; Rb[x[i].pos1]=Sum(cur,arr2); Add(cur,1,arr2); } LL ans=0; for(int i=1; i<=n; ++i) { LL l=(i-1)-Ls[i],r=(n-i)-Rb[i]; ans+=l*r+Ls[i]*Rb[i]; } printf("%lld\n",ans); } return 0; }